initial import
authorAleš Křenek <ljocha@ics.muni.cz>
Fri, 18 Jun 2004 11:45:49 +0000 (11:45 +0000)
committerAleš Křenek <ljocha@ics.muni.cz>
Fri, 18 Jun 2004 11:45:49 +0000 (11:45 +0000)
139 files changed:
org.glite.lb.client-interface/Makefile [new file with mode: 0644]
org.glite.lb.client-interface/build.xml [new file with mode: 0755]
org.glite.lb.client-interface/interface/consumer.h [new file with mode: 0644]
org.glite.lb.client-interface/interface/context.h [new file with mode: 0644]
org.glite.lb.client-interface/interface/dump.h [new file with mode: 0644]
org.glite.lb.client-interface/interface/events.h.T [new file with mode: 0644]
org.glite.lb.client-interface/interface/jobstat.h.T [new file with mode: 0644]
org.glite.lb.client-interface/interface/load.h [new file with mode: 0644]
org.glite.lb.client-interface/interface/notification.h [new file with mode: 0644]
org.glite.lb.client-interface/interface/notifid.h [new file with mode: 0644]
org.glite.lb.client-interface/interface/producer.h.T [new file with mode: 0644]
org.glite.lb.client-interface/interface/purge.h [new file with mode: 0644]
org.glite.lb.client-interface/project/properties.xml [new file with mode: 0755]
org.glite.lb.client/.Makefile.swp [new file with mode: 0644]
org.glite.lb.client/Makefile [new file with mode: 0644]
org.glite.lb.client/build.xml [new file with mode: 0755]
org.glite.lb.client/interface/CountRef.h [new file with mode: 0644]
org.glite.lb.client/interface/Event.h.T [new file with mode: 0644]
org.glite.lb.client/interface/Job.h [new file with mode: 0644]
org.glite.lb.client/interface/JobStatus.h.T [new file with mode: 0644]
org.glite.lb.client/interface/LoggingExceptions.h [new file with mode: 0644]
org.glite.lb.client/interface/Notification.h [new file with mode: 0644]
org.glite.lb.client/interface/ServerConnection.h [new file with mode: 0644]
org.glite.lb.client/project/properties.xml [new file with mode: 0755]
org.glite.lb.client/project/taskdefs.xml [new file with mode: 0755]
org.glite.lb.client/src/Event.cpp.T [new file with mode: 0644]
org.glite.lb.client/src/Job.cpp [new file with mode: 0644]
org.glite.lb.client/src/JobStatus.cpp.T [new file with mode: 0644]
org.glite.lb.client/src/Notification.cpp [new file with mode: 0644]
org.glite.lb.client/src/ServerConnection.cpp [new file with mode: 0644]
org.glite.lb.client/src/args.c.T [new file with mode: 0644]
org.glite.lb.client/src/args.h [new file with mode: 0644]
org.glite.lb.client/src/connection.c [new file with mode: 0644]
org.glite.lb.client/src/connection.h [new file with mode: 0644]
org.glite.lb.client/src/consumer.c [new file with mode: 0644]
org.glite.lb.client/src/dump.c [new file with mode: 0644]
org.glite.lb.client/src/load.c [new file with mode: 0644]
org.glite.lb.client/src/logevent.c.T [new file with mode: 0644]
org.glite.lb.client/src/notification.c [new file with mode: 0644]
org.glite.lb.client/src/prod_proto.c [new file with mode: 0644]
org.glite.lb.client/src/prod_proto.h [new file with mode: 0644]
org.glite.lb.client/src/producer.c [new file with mode: 0644]
org.glite.lb.client/src/purge.c [new file with mode: 0644]
org.glite.lb.client/src/uiwrap.c.T [new file with mode: 0644]
org.glite.lb.common/Makefile [new file with mode: 0644]
org.glite.lb.common/build.xml [new file with mode: 0755]
org.glite.lb.common/interface/authz.h [new file with mode: 0644]
org.glite.lb.common/interface/context-int.h [new file with mode: 0644]
org.glite.lb.common/interface/dgssl.h [new file with mode: 0644]
org.glite.lb.common/interface/escape.h [new file with mode: 0644]
org.glite.lb.common/interface/events_parse.h [new file with mode: 0644]
org.glite.lb.common/interface/il_string.h [new file with mode: 0644]
org.glite.lb.common/interface/log_proto.h [new file with mode: 0644]
org.glite.lb.common/interface/mini_http.h [new file with mode: 0644]
org.glite.lb.common/interface/trio.h [new file with mode: 0644]
org.glite.lb.common/interface/ulm_parse.h [new file with mode: 0644]
org.glite.lb.common/interface/xml_conversions.h [new file with mode: 0644]
org.glite.lb.common/interface/xml_parse.h [new file with mode: 0644]
org.glite.lb.common/project/properties.xml [new file with mode: 0755]
org.glite.lb.common/src/context.c [new file with mode: 0644]
org.glite.lb.common/src/dgssl.c [new file with mode: 0644]
org.glite.lb.common/src/escape.c [new file with mode: 0644]
org.glite.lb.common/src/events.c.T [new file with mode: 0644]
org.glite.lb.common/src/events_parse.c.T [new file with mode: 0644]
org.glite.lb.common/src/il_int.c [new file with mode: 0644]
org.glite.lb.common/src/il_log.c [new file with mode: 0644]
org.glite.lb.common/src/il_msg.c [new file with mode: 0644]
org.glite.lb.common/src/il_string.c [new file with mode: 0644]
org.glite.lb.common/src/mini_http.c [new file with mode: 0644]
org.glite.lb.common/src/notifid.c [new file with mode: 0644]
org.glite.lb.common/src/notifid.h [new file with mode: 0644]
org.glite.lb.common/src/param.c [new file with mode: 0644]
org.glite.lb.common/src/query_rec.c [new file with mode: 0644]
org.glite.lb.common/src/status.c.T [new file with mode: 0644]
org.glite.lb.common/src/strio.c [new file with mode: 0644]
org.glite.lb.common/src/strio.h [new file with mode: 0644]
org.glite.lb.common/src/trio.c [new file with mode: 0644]
org.glite.lb.common/src/triop.h [new file with mode: 0644]
org.glite.lb.common/src/ulm_parse.c [new file with mode: 0644]
org.glite.lb.common/src/xml_conversions.c [new file with mode: 0644]
org.glite.lb.common/src/xml_parse.c.T [new file with mode: 0644]
org.glite.lb.server/Makefile [new file with mode: 0644]
org.glite.lb.server/build.xml [new file with mode: 0755]
org.glite.lb.server/project/properties.xml [new file with mode: 0755]
org.glite.lb.server/src/bkindex.c [new file with mode: 0644]
org.glite.lb.server/src/bkserverd.c [new file with mode: 0644]
org.glite.lb.server/src/db_store.c [new file with mode: 0644]
org.glite.lb.server/src/dump.c [new file with mode: 0644]
org.glite.lb.server/src/get_events.c.T [new file with mode: 0644]
org.glite.lb.server/src/get_events.h [new file with mode: 0644]
org.glite.lb.server/src/il_notification.c [new file with mode: 0644]
org.glite.lb.server/src/il_notification.h [new file with mode: 0644]
org.glite.lb.server/src/index.c.T [new file with mode: 0644]
org.glite.lb.server/src/index.h [new file with mode: 0644]
org.glite.lb.server/src/index_lex.l [new file with mode: 0644]
org.glite.lb.server/src/index_parse.y [new file with mode: 0644]
org.glite.lb.server/src/jobstat.c [new file with mode: 0644]
org.glite.lb.server/src/jobstat.h [new file with mode: 0644]
org.glite.lb.server/src/jobstat_supp.c [new file with mode: 0644]
org.glite.lb.server/src/lb_authz.c [new file with mode: 0644]
org.glite.lb.server/src/lb_authz.h [new file with mode: 0644]
org.glite.lb.server/src/lb_html.c [new file with mode: 0644]
org.glite.lb.server/src/lb_html.h [new file with mode: 0644]
org.glite.lb.server/src/lb_http.c [new file with mode: 0644]
org.glite.lb.server/src/lb_http.h [new file with mode: 0644]
org.glite.lb.server/src/lb_proto.c [new file with mode: 0644]
org.glite.lb.server/src/lb_proto.h [new file with mode: 0644]
org.glite.lb.server/src/lb_xml_parse.c.T [new file with mode: 0644]
org.glite.lb.server/src/lb_xml_parse.h [new file with mode: 0644]
org.glite.lb.server/src/lbs_db.c [new file with mode: 0644]
org.glite.lb.server/src/lbs_db.h [new file with mode: 0644]
org.glite.lb.server/src/load.c [new file with mode: 0644]
org.glite.lb.server/src/lock.c [new file with mode: 0644]
org.glite.lb.server/src/lock.h [new file with mode: 0644]
org.glite.lb.server/src/notif_match.c [new file with mode: 0644]
org.glite.lb.server/src/notification.c [new file with mode: 0644]
org.glite.lb.server/src/openserver.c [new file with mode: 0644]
org.glite.lb.server/src/purge.h [new file with mode: 0644]
org.glite.lb.server/src/query.c [new file with mode: 0644]
org.glite.lb.server/src/query.h [new file with mode: 0644]
org.glite.lb.server/src/request.c [new file with mode: 0644]
org.glite.lb.server/src/server_state.c [new file with mode: 0644]
org.glite.lb.server/src/server_state.h [new file with mode: 0644]
org.glite.lb.server/src/srv_purge.c [new file with mode: 0644]
org.glite.lb.server/src/store.c.T [new file with mode: 0644]
org.glite.lb.server/src/store.h [new file with mode: 0644]
org.glite.lb.server/src/stored_master.c [new file with mode: 0644]
org.glite.lb.server/src/userjobs.c [new file with mode: 0644]
org.glite.lb.server/src/write2rgma.c [new file with mode: 0755]
org.glite.lb/build.xml [new file with mode: 0755]
org.glite.lb/project/MultiStruct.pm [new file with mode: 0644]
org.glite.lb/project/StructField.pm [new file with mode: 0644]
org.glite.lb/project/at3 [new file with mode: 0755]
org.glite.lb/project/dependencies.properties [new file with mode: 0644]
org.glite.lb/project/events.T [new file with mode: 0644]
org.glite.lb/project/properties.xml [new file with mode: 0755]
org.glite.lb/project/status.T [new file with mode: 0644]
org.glite.lb/project/taskdefs.xml [new file with mode: 0755]
org.glite.lb/project/types.T [new file with mode: 0644]

diff --git a/org.glite.lb.client-interface/Makefile b/org.glite.lb.client-interface/Makefile
new file mode 100644 (file)
index 0000000..0d501a0
--- /dev/null
@@ -0,0 +1,33 @@
+include Makefile.inc
+
+SUFFIXES = .T
+
+VPATH=${interface}
+AT3=perl -I${lbconfig} ${lbproject}/at3
+
+STAGETO=${stageinc}/${globalprefix}/${lbprefix}
+STATIC_H=consumer.h context.h dump.h load.h notification.h notifid.h purge.h
+GEN_H=events.h jobstat.h producer.h
+
+all stage: stage-gen stage-static
+compile: generate
+
+stage-static:
+       -mkdir -p ${STAGETO}
+       cd ${interface} && install -m 644 ${STATIC_H} ${STAGETO}
+
+stage-gen: generate
+       -mkdir -p ${STAGETO}
+       install -m 644 ${GEN_H} ${STAGETO}
+
+generate: ${GEN_H}
+
+check: 
+       @echo No unit test required for interface-only module.
+
+%.h: %.h.T
+       rm -f $@
+       ${AT3} $< >$@ || rm -f $@
+       chmod -w $@ >/dev/null
+
+
diff --git a/org.glite.lb.client-interface/build.xml b/org.glite.lb.client-interface/build.xml
new file mode 100755 (executable)
index 0000000..4aa4105
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="lb" default="dist">
+       
+       <import file="../org.glite/project/baseline.properties.xml" />
+       <import file="./project/properties.xml"/>
+       <import file="${subsystem.properties.file}"/>
+       <import file="${global.properties.file}" />
+
+       <property file="${user.dependencies.file}"/>
+       <property file="${component.dependencies.file}" />
+       <property file="${subsystem.dependencies.file}" />
+       <property file="${global.dependencies.file}"/>
+       
+       <import file="${subsystem.taskdefs.file}" />
+       <import file="${global.taskdefs.file}" />
+
+       <import file="${global.targets-external-dependencies.file}"/>   
+       <import file="${global.targets-make.file}" />
+               
+       <property file="${module.version.file}"/>
+
+       <target name="localinit"
+               description="Module specific initialization tasks">
+               <antcall target="lbmakefiles"/>
+       </target>
+
+       <target name="localcompile"
+               description="Module specific compile tasks">
+       </target>
+       
+       <target name="localclean"
+               description="Module specific cleaning tasks">
+       </target>
+       
+</project>             
diff --git a/org.glite.lb.client-interface/interface/consumer.h b/org.glite.lb.client-interface/interface/consumer.h
new file mode 100644 (file)
index 0000000..0b7b63a
--- /dev/null
@@ -0,0 +1,290 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_CONSUMER_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_CONSUMER_H__
+
+/*!
+ * \file client/consumer.h  (lbapi.h originaly)
+ * \brief L&B consumer API
+ *
+ * General rules:
+ * - functions return 0 on success, nonzero on error, errror details can 
+ *   be found via edg_wll_ErrorCode()
+ * - OUT are ** types, functions malloc()-ate objects and fill in the pointer 
+ *   pointed to by the OUT argument
+ * - returned lists of pointers are NULL-terminated malloc()-ed arrays
+ * - edg_wll_Query + wrapper terminate arrays with EDG_WLL_EVENT_UNDEF event
+ * - OUT is NULL if the list is empty
+ */
+
+#ident "$Header$"
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/context.h"
+#include "glite/lb/events.h"
+#include "glite/lb/jobstat.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Predefined types for query attributes
+ */
+typedef enum _edg_wll_QueryAttr{
+       EDG_WLL_QUERY_ATTR_UNDEF=0,     /**< Not-defined value, used to terminate lists etc. */
+       EDG_WLL_QUERY_ATTR_JOBID,       /**< Job Id \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_ATTR_OWNER,       /**< Job owner \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_ATTR_STATUS,      /**< Current job status */
+       EDG_WLL_QUERY_ATTR_LOCATION,    /**< Where is the job processed */
+       EDG_WLL_QUERY_ATTR_DESTINATION, /**< Destination CE */
+       EDG_WLL_QUERY_ATTR_DONECODE,    /**< Minor done status (OK,fail,cancel) */
+       EDG_WLL_QUERY_ATTR_USERTAG,     /**< User tag */
+       EDG_WLL_QUERY_ATTR_TIME,        /**< Timestamp \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_ATTR_LEVEL,       /**< Logging level (see "dglog.h") * \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_ATTR_HOST,        /**< Where the event was generated */
+       EDG_WLL_QUERY_ATTR_SOURCE,      /**< Source component */
+       EDG_WLL_QUERY_ATTR_INSTANCE,    /**< Instance of the source component */
+       EDG_WLL_QUERY_ATTR_EVENT_TYPE,  /**< Event type \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_ATTR_CHKPT_TAG,   /**< Checkpoint tag */
+       EDG_WLL_QUERY_ATTR_RESUBMITTED, /**< Job was resubmitted */
+       EDG_WLL_QUERY_ATTR_PARENT,      /**< Job was resubmitted */
+       EDG_WLL_QUERY_ATTR_EXITCODE,    /**< Unix exit code */
+       EDG_WLL_QUERY_ATTR__LAST
+/*     if adding new attribute, add conversion string to common/xml_conversions.c too !! */
+} edg_wll_QueryAttr;
+
+
+/*!
+ * Predefined types for query operands
+ */
+typedef enum _edg_wll_QueryOp{
+       EDG_WLL_QUERY_OP_EQUAL,         /**< attribute is equal to the operand value \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_OP_LESS,          /**< attribute is grater than the operand value \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_OP_GREATER,       /**< attribute is less than the operand value \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_OP_WITHIN,        /**< attribute is in given interval \see _edg_wll_QueryRec */
+       EDG_WLL_QUERY_OP_UNEQUAL,       /**< attribute is not equal to the operand value \see _edg_wll_QueryRec */
+} edg_wll_QueryOp;
+
+
+/*!
+ * Single query condition for edg_wll_Query().
+ * Those records are composed to form an SQL \a where clause
+ * when processed at the L&B server
+ */
+typedef struct _edg_wll_QueryRec {
+       edg_wll_QueryAttr       attr;   /**< attribute to query */
+       edg_wll_QueryOp         op;     /**< query operation */
+
+/**
+ * Specification of attribute to query
+ */
+       union {
+               char *                  tag;    /**< user tag name */
+               edg_wll_JobStatCode     state;  /**< job status code */ 
+       } attr_id;
+/**
+ * Query operand.
+ * The appropriate type is uniquely defined by the attr member
+ */
+       union {
+               int     i;      /**< integer query attribute value */
+               char    *c;     /**< character query attribute value */
+               struct timeval  t;      /**< time query attribute value */
+               edg_wlc_JobId   j;      /**< JobId query attribute value */
+       } value, value2;
+} edg_wll_QueryRec;
+
+/************************************************
+ * API FUNCTION DECLARATIONS                   *
+ */
+
+
+#ifdef CLIENT_SBIN_PROG
+extern int edg_wll_http_send_recv(
+       edg_wll_Context,
+       char *, const char * const *, char *,
+       char **,char ***,char **
+);
+
+extern int http_check_status(
+       edg_wll_Context,
+       char *,
+       char **
+);
+
+extern int set_server_name_and_port(
+       edg_wll_Context,
+       const edg_wll_QueryRec **
+);
+
+#endif
+
+/**
+ * \name Server querying
+ *
+ *@{
+ */
+
+/**
+ * General query on events.
+ * Return events satysfying all conditions 
+ * query records represent conditions in the form 
+ * \a attr \a op \a value eg. time > 87654321.
+ * \see edg_wll_QueryRec
+ *
+ * \param context IN: context to work with
+ * \param job_conditions IN: query conditions (ANDed) on current job status, null (i.e. ATTR_UNDEF) terminated list. NULL means empty list, i.e. always TRUE
+ * \param event_conditions: conditions on events, null terminated list, NULL means empty list, i.e. always TRUE
+ * \param events OUT: list of matching events
+ */
+int edg_wll_QueryEvents(
+       edg_wll_Context                 context,
+       const edg_wll_QueryRec *        job_conditions,
+       const edg_wll_QueryRec *        event_conditions,
+       edg_wll_Event **                events
+);
+
+int edg_wll_QueryEventsExt(
+       edg_wll_Context                 context,
+       const edg_wll_QueryRec **       job_conditions,
+       const edg_wll_QueryRec **       event_conditions,
+       edg_wll_Event **                events
+);
+
+/** 
+ * General query on jobs.
+ * Return jobs (and possibly their states) for which an event satisfying the conditions
+ * exists.
+ * \see edg_wll_QueryEvents
+ * \param context IN: context to work with
+ * \param conditions IN: query records (ANDed), null (i.e. EDG_WLL_ATTR_UNDEF) terminated list
+ * \param flags IN: additional status fields to retrieve (\see edg_wll_JobStatus)
+ * \param jobs OUT: list of job ids. May be NULL.
+ * \param states OUT: list of corresponding states (returned only if not NULL)
+ */
+int edg_wll_QueryJobs(
+       edg_wll_Context                 context,
+       const edg_wll_QueryRec *        conditions,
+       int                             flags,
+       edg_wlc_JobId **                jobs,
+       edg_wll_JobStat **              states
+);
+
+int edg_wll_QueryJobsExt(
+       edg_wll_Context                 context,
+       const edg_wll_QueryRec **       conditions,
+       int                             flags,
+       edg_wlc_JobId **                jobs,
+       edg_wll_JobStat **              states
+);
+
+/**
+ * Bitmasks for edg_wll_JobStatus() flags argument.
+ * Settings these flags causes the status calls to retrieve additional
+ * information.
+ */
+#define EDG_WLL_STAT_CLASSADS  1       /**< various job description fields */
+#define EDG_WLL_STAT_CHILDREN  2       /**< list of subjob JobId's */
+#define EDG_WLL_STAT_CHILDSTAT 4       /**< apply the flags recursively to subjobs */
+/* starting from bit 10 private flags begins - do not add 1024 and more! */
+
+/** Return status of a single job.
+ * \param context IN: context to operate on
+ * \param jobid IN: query this job
+ * \param flags IN: specifies optional status fields to retrieve,
+ *     \see EDG_WLL_STAT_CLASSADS, EDG_WLL_STAT_CHILDREN, EDG_WLL_STAT_CHILDSTAT
+ */
+
+int edg_wll_JobStatus(
+       edg_wll_Context         context,
+       const edg_wlc_JobId             jobid,
+       int                     flags,
+       edg_wll_JobStat         *status
+);
+
+/**
+ * Return all events related to a single job.
+ * Convenience wrapper around edg_wll_Query()
+ * \param context IN: context to work with
+ * \param jobId IN: job to query
+ * \param events OUT: list of events 
+ */
+
+int edg_wll_JobLog(
+       edg_wll_Context         context,
+       const edg_wlc_JobId     jobId,
+       edg_wll_Event **        events
+);
+
+/**
+ * All current user's jobs.
+ * \param context IN: context to work with
+ * \param jobs OUT: list of the user's jobs
+ * \param states OUT: list of the jobs' states
+ */
+int edg_wll_UserJobs(
+       edg_wll_Context         context,
+       edg_wlc_JobId **        jobs,
+       edg_wll_JobStat **      states
+);
+
+/**
+ * Server supported indexed attributes
+ * \see DataGrid-01-TEN-0125
+ * \param context IN: context to work with
+ * \param attrs OUT: configured indices (each index is an UNDEF-terminated
+ *             array of QueryRec's from which only attr (and attr_id 
+ *             eventually) are meaningful
+ */
+int edg_wll_GetIndexedAttrs(
+       edg_wll_Context         context,
+       edg_wll_QueryRec        ***attrs
+);
+
+/**
+ * Retrieve limit on query result size (no. of events or jobs).
+ * FIXME: not implemented.
+ * \see DataGrid-01-TEN-0125
+ * \param context IN: context to work with
+ * \param limit OUT: server imposed limit
+ */
+int edg_wll_GetServerLimit(
+       edg_wll_Context context,
+       int             *limit
+);
+
+/**
+ * UI port for the job
+ * \param context IN: context to work with
+ * \param jobId IN: job to query
+ * \param name IN: name of the UI-port
+ * \param host OUT: hostname of port
+ * \param port OUT: port number
+ */
+int edg_wll_QueryListener(
+       edg_wll_Context context,
+       edg_wlc_JobId           jobId,
+       const char *    name,
+       char **         host,
+       uint16_t *      port
+);
+
+/*@}*/
+
+/*
+ * edg_wll_QueryRec manipulation
+ */
+
+/** Free edg_wll_QueryRec internals, not the structure itself */
+void edg_wll_QueryRecFree(edg_wll_QueryRec *);
+
+/**
+ * default and maximal query timeout (in seconds)
+ */
+#define EDG_WLL_QUERY_TIMEOUT_DEFAULT   120
+#define EDG_WLL_QUERY_TIMEOUT_MAX       1800
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_CONSUMER_H__ */
diff --git a/org.glite.lb.client-interface/interface/context.h b/org.glite.lb.client-interface/interface/context.h
new file mode 100644 (file)
index 0000000..1560924
--- /dev/null
@@ -0,0 +1,214 @@
+#ifndef _EDG_WORKLOAD_LOGGING_CLIENT_CONTEXT_H
+#define _EDG_WORKLOAD_LOGGING_CLIENT_CONTEXT_H
+
+/**
+ * \file edg/workload/logging/client/context.h
+ * \brief L&B API common context (publicly visible) and related definitions
+ */
+
+#include "glite/wms/common/utilities/exception_codes.h"
+#include "glite/wms/jobid/cjobid.h"
+
+#ident "$Header$"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Opaque context type */
+typedef struct _edg_wll_Context *edg_wll_Context;
+
+/** Constants defining the context parameters */
+typedef enum _edg_wll_ContextParam {
+       EDG_WLL_PARAM_HOST,             /**< hostname to appear as event orgin */
+       EDG_WLL_PARAM_SOURCE,           /**< event source component */
+       EDG_WLL_PARAM_INSTANCE,         /**< instance of the source component */
+       EDG_WLL_PARAM_LEVEL,            /**< logging level */
+       EDG_WLL_PARAM_DESTINATION,      /**< logging destination host */
+       EDG_WLL_PARAM_DESTINATION_PORT, /**< logging destination port */
+       EDG_WLL_PARAM_LOG_TIMEOUT,      /**< logging timeout (asynchronous) */
+       EDG_WLL_PARAM_LOG_SYNC_TIMEOUT, /**< logging timeout (synchronous) */
+       EDG_WLL_PARAM_QUERY_SERVER,     /**< default server name to query */
+       EDG_WLL_PARAM_QUERY_SERVER_PORT,/**< default server port to query */
+       EDG_WLL_PARAM_QUERY_SERVER_OVERRIDE,/**< host:port parameter setting override even values in jobid (useful for debugging & hacking only) */
+       EDG_WLL_PARAM_QUERY_TIMEOUT,    /**< query timeout */
+       EDG_WLL_PARAM_QUERY_JOBS_LIMIT, /**< maximal query jobs result size */
+       EDG_WLL_PARAM_QUERY_EVENTS_LIMIT,/**< maximal query events result size */
+       EDG_WLL_PARAM_QUERY_RESULTS,    /**< maximal query result size */
+       EDG_WLL_PARAM_QUERY_CONNECTIONS,/**< maximal number of open connections in ctx->connPoll */
+       EDG_WLL_PARAM_NOTIF_SERVER,     /**< default notification server name */
+       EDG_WLL_PARAM_NOTIF_SERVER_PORT,/**< default notification server port */
+       EDG_WLL_PARAM_NOTIF_TIMEOUT,    /**< notif timeout */
+       EDG_WLL_PARAM_X509_PROXY,       /**< proxy file to use for authentication */
+       EDG_WLL_PARAM_X509_KEY,         /**< key file to use for authentication */
+       EDG_WLL_PARAM_X509_CERT,        /**< certificate file to use for authentication */
+       EDG_WLL_PARAM__LAST,            /**< marker, LB internal use only */
+} edg_wll_ContextParam;
+
+/** sets returned query results */
+typedef enum _edg_wll_QueryResults {
+       EDG_WLL_QUERYRES_UNDEF,         /* uninitialized value */
+       EDG_WLL_QUERYRES_NONE,
+       EDG_WLL_QUERYRES_ALL,
+       EDG_WLL_QUERYRES_LIMITED,
+       EDG_WLL_QUERYRES__LAST          /* marker, for internal use only */
+} edg_wll_QueryResults;
+
+/** identification of logging component */
+typedef enum _edg_wll_Source {
+       EDG_WLL_SOURCE_NONE,            /* uninitialized value */
+       EDG_WLL_SOURCE_USER_INTERFACE,
+       EDG_WLL_SOURCE_NETWORK_SERVER,
+       EDG_WLL_SOURCE_WORKLOAD_MANAGER,
+       EDG_WLL_SOURCE_BIG_HELPER,
+       EDG_WLL_SOURCE_JOB_SUBMISSION,
+       EDG_WLL_SOURCE_LOG_MONITOR,
+       EDG_WLL_SOURCE_LRMS,
+       EDG_WLL_SOURCE_APPLICATION,
+       EDG_WLL_SOURCE__LAST            /* marker, for internal use only */
+} edg_wll_Source;
+
+
+/** Allocate an initialize a new context object.
+ * \param context OUT returned context
+ * \return 0 on success, ENOMEM if malloc() fails
+ */
+int edg_wll_InitContext(edg_wll_Context *context);
+
+/** Destroy and free context object.
+ * Also performs necessary cleanup (closing connections etc.)
+ * \param context IN context to free
+ */
+void edg_wll_FreeContext(edg_wll_Context context);
+
+/** Set a context parameter.
+ * \param context INOUT context to work with
+ * \param param IN parameter to set
+ * \param ... IN value to set (if NULL or 0, default is used)
+ * \retval 0 success
+ * \retval EINVAL param is not a valid parameter, or invalid value
+ */
+int edg_wll_SetParam(
+       edg_wll_Context context,
+       edg_wll_ContextParam param,
+       ...
+);
+
+struct timeval;        /* gcc, shut up! */
+
+int edg_wll_SetParamInt(edg_wll_Context,edg_wll_ContextParam,int);
+int edg_wll_SetParamString(edg_wll_Context,edg_wll_ContextParam,const char *);
+int edg_wll_SetParamTime(edg_wll_Context,edg_wll_ContextParam,const struct timeval *);
+
+/** Get current parameter value.
+ * \param context INOUT context to work with
+ * \param param IN parameter to retrieve
+ * \param ... OUT pointer to output variable
+ * \retval 0 success
+ * \retval EINVAL param is not a valid parameter
+ */
+int edg_wll_GetParam(
+       edg_wll_Context context,
+       edg_wll_ContextParam param,
+       ...
+);
+
+
+/**
+ * L&B subsystem specific error codes.
+ * Besides them L&B functions return standard \a errno codes in their usual
+ * meaning.
+ */
+
+/* XXX: cleanup required */
+
+typedef enum _edg_wll_ErrorCode {
+/** Base for L&B specific code. Use the constant from common/ */
+       EDG_WLL_ERROR_BASE = GLITE_WMS_LOGGING_ERROR_BASE,
+       EDG_WLL_ERROR_PARSE_BROKEN_ULM, /**< Parsing ULM line into edg_wll_Event structure */
+       EDG_WLL_ERROR_PARSE_EVENT_UNDEF, /**< Undefined event name */
+       EDG_WLL_ERROR_PARSE_MSG_INCOMPLETE, /**< Incomplete message (missing fields) */
+       EDG_WLL_ERROR_PARSE_KEY_DUPLICITY, /**< Duplicate entry in message */
+       EDG_WLL_ERROR_PARSE_KEY_MISUSE, /**< Entry not allowed for this message type */
+       EDG_WLL_ERROR_PARSE_OK_WITH_EXTRA_FIELDS, /**< Additional, not understood fields found in message.
+               The rest is OK therefore this is not a true error. */
+        EDG_WLL_ERROR_XML_PARSE, /**< Error in parsing XML protocol. */
+        EDG_WLL_ERROR_SERVER_RESPONSE, /**< Generic failure on server. See syslog on the server machine for details. */
+        EDG_WLL_ERROR_JOBID_FORMAT, /**< Malformed jobid */
+       EDG_WLL_ERROR_DB_CALL, /**< Failure of underlying database engine.
+               See errDesc returned by edg_wll_ErrorCode(). */
+       EDG_WLL_ERROR_URL_FORMAT, /**< Malformed URL */
+       EDG_WLL_ERROR_MD5_CLASH, /**< MD5 hash same for different strings. Very unlikely :-). */
+       EDG_WLL_ERROR_SSL, /**< Generic SSL error. See errDesc returned by edg_wll_Error(). */
+       EDG_WLL_ERROR_DNS, /**< DNS resolver error. See errDesc returned by edg_wll_Error(). */
+       EDG_WLL_ERROR_NOJOBID,  /**< Attmepted call requires calling edg_wll_SetLoggingJob() first. */
+       EDG_WLL_ERROR_NOINDEX,  /**< Query does not contain any conidion on indexed attribute. */
+       EDG_WLL_IL_PROTO,       /**< Interlogger to lbserver communication protocol error. */
+       EDG_WLL_IL_SYS,         /**< Interlogger internal error. */
+       EDG_WLL_IL_EVENTS_WAITING, /**< Interlogger still has events pending delivery. */
+} edg_wll_ErrorCode;
+
+/**
+ * Retrieve error details on recent API call
+ * \param context IN: context to work with
+ * \param errText OUT: standard error text
+ *      (may be NULL - no text returned)
+ * \param errDesc OUT: additional error description
+ *      (may be NULL - no text returned)
+ * \return Error code of the recent error
+ */
+
+int edg_wll_Error(
+       edg_wll_Context context,
+       char            **errText,
+       char            **eddDesc
+);
+
+/** Convert source code to printable string 
+ */
+char * edg_wll_SourceToString(edg_wll_Source src);
+
+/** Convert name to source code
+ * \return Matching code or EDG_WLL_SOURCE_NONE
+ */
+edg_wll_Source edg_wll_StringToSource(const char *name);
+
+/** Convert Query result code to printable string 
+ */
+char * edg_wll_QResultToString(edg_wll_QueryResults res);
+
+/** Convert name to Query result code
+ * \return Matching code or EDG_WLL_SOURCE_NONE
+ */
+edg_wll_QueryResults edg_wll_StringToQResult(const char *name);
+
+/**
+ * type of sequence code (used when setting to the context)
+ */
+#define EDG_WLL_SEQ_NORMAL      1
+#define EDG_WLL_SEQ_DUPLICATE   11
+
+/**
+ * initial sequence code for BigHelper
+ */
+
+#define EDG_WLL_SEQ_BIGHELPER_INITIAL "UI=2:NS=0:WM=0:BH=1:JSS=0:LM=0:LRMS=0:APP=0"
+
+/** Retrieve current sequence code from the context */
+char * edg_wll_GetSequenceCode(
+       const edg_wll_Context   context
+);
+
+/**
+ * retrieve the current logging JobId from the context
+ */
+int edg_wll_GetLoggingJob(
+       const edg_wll_Context   context,
+       edg_wlc_JobId   *jobid_out
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif 
diff --git a/org.glite.lb.client-interface/interface/dump.h b/org.glite.lb.client-interface/interface/dump.h
new file mode 100644 (file)
index 0000000..37f9dd7
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_DUMP_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_DUMP_H__
+
+#ident "$Header$"
+
+#define EDG_WLL_DUMP_NOW       -1
+#define EDG_WLL_DUMP_LAST_START        -2
+#define EDG_WLL_DUMP_LAST_END  -3
+/*      if adding new attribute, add conversion string to common/xml_conversions.c too !! */
+
+typedef struct {
+       time_t  from,to;
+} edg_wll_DumpRequest;
+
+typedef struct {
+       char    *server_file;
+       time_t  from,to;
+} edg_wll_DumpResult;
+
+/** Dump events in a given time interval
+ */
+
+int edg_wll_DumpEvents(
+       edg_wll_Context,
+       const edg_wll_DumpRequest *,
+       edg_wll_DumpResult *
+);
+
+
+#endif
+
diff --git a/org.glite.lb.client-interface/interface/events.h.T b/org.glite.lb.client-interface/interface/events.h.T
new file mode 100644 (file)
index 0000000..971d916
--- /dev/null
@@ -0,0 +1,332 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_EVENTS_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_EVENTS_H__
+
+/**
+ * \file edg/workload/logging/client/events.h
+ * \brief contains definition of event type codes for use both by lbapi.h and dglog.h
+ */
+
+#ident "$Header$"
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+#include <sys/time.h>
+#include <inttypes.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/context.h"
+#include "glite/lb/notifid.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Predefined type for ULM string 
+ */
+typedef char *edg_wll_LogLine;
+
+
+/**
+ * \typedef edg_wll_EventCode
+ * Predefined event types 
+ */
+typedef enum _edg_wll_EventCode {
+/** invalid code, e.g. uninitialized variable */
+        EDG_WLL_EVENT_UNDEF = 0,
+@@@{
+for my $e (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $u = uc $e;
+       my $c = getTypeComment $event $e;
+       gen "\tEDG_WLL_EVENT_$u,\t/**< $c */\n";
+}
+@@@}
+        EDG_WLL_EVENT__LAST,   /**< last currently supported event code */
+} edg_wll_EventCode;
+
+/**
+ * \fn edg_wll_EventCode edg_wll_StringToEvent(char *name)
+ * \param name         a string event name (e.g. "JobTransfer")
+ * \return corresponding numeric code (edg_wll_EventCode)
+ * \brief convert a string event name to the corresponding numeric code
+ */
+
+extern edg_wll_EventCode edg_wll_StringToEvent(char *);
+
+/**
+ * \fn char *edg_wll_EventToString(edg_wll_EventCode event)
+ * \param event                an event numeric code (edg_wll_EventCode)
+ * \return corresponding string (e.g. "JobTransfer")
+ * \brief convert an event numeric code to the corresponding string
+ */
+
+extern char *edg_wll_EventToString(edg_wll_EventCode);
+
+
+/**
+ * \typedef edg_wll_KeyNameCode
+ * Predefined ULM key types
+ */
+typedef enum _edg_wll_KeyNameCode {
+       UNDEFINED,              /**< undefined */
+       EDG_WLL_EVNT,           /**< event type */
+@@@{
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       my $fn = getName $f 'ULM';
+       my $fnu = uc $fn;
+       my $c = $f->{comment};
+       if (hasAlias $f 'ULM') {
+               gen "\tULM\_$fnu,\t\t/**< $c */\n";
+       } else {
+               gen "\tEDG_WLL\_COMMON\_$fnu,\t\t/**< $c */\n";
+       }
+}
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fnu = uc $f->{name};
+               my $c = $f->{comment};
+               gen "\tEDG_WLL\_$tu\_$fnu,\t/**< $c */\n";
+       }
+}
+@@@}
+       EDG_WLL_INTERNAL_TYPE,          /**< internal message type */
+} edg_wll_KeyNameCode;
+
+/**
+ * \fn edg_wll_KeyNameCode edg_wll_StringToKeyName(char *name)
+ * \param name         a string ULM key name (e.g. "DG.JOB.TRANSFER.DEST")
+ * \return corresponding numeric code (edg_wll_KeyNameCode)
+ * \brief convert a string ULM key name to the corresponding numeric code
+ */
+
+extern edg_wll_KeyNameCode edg_wll_StringToKeyName(char *);
+
+/**
+ * \fn char *edg_wll_KeyNameToString(edg_wll_KeyNameCode key)
+ * \param key          a ULM key name numeric code (edg_wll_KeyNameCode)
+ * \return corresponding string (e.g. "DG.JOB.TRANSFER.DEST")
+ * \brief convert a ULM key name numeric code to the corresponding string
+ */
+
+extern char *edg_wll_KeyNameToString(edg_wll_KeyNameCode);
+
+
+/**
+ * Predefined _code_ types and
+ * related StringTo_code and _code_ToString function prototypes
+ */
+@@@{
+$indent = "\t";
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       if ($f->{codes}) {
+               my $fn = ucfirst($f->{name});
+               my $fnu = uc $fn;
+               my $c = "${fn}"; # new code
+               my $enum = "enum edg_wll\_$c"; # new enum name
+
+# enum
+               gen qq{
+/**
+ * \\enum $enum
+ * $fn codes 
+ */
+$enum \{
+};
+               gen $indent."EDG_WLL_${fnu}_UNDEFINED,\t/**< undefined code */ \n";
+               for (@{$f->{codes}}) {
+                       gen $indent."EDG_WLL_${fnu}_$_->{name},\t/**< $_->{comment} */ \n";
+               }
+               gen "};\n";
+
+# function StringTo:
+               gen qq{
+/**
+ * \\fn $enum edg_wll_StringTo${c}(char *name);
+ * \\param name                a string representing $fn code (e.g. \"${$f->{codes}}[1]->{name}\")
+ * \\return corresponding numeric code ($enum)
+ * \\brief converts a string $fn code to corresponding numeric code
+ */
+extern $enum edg_wll_StringTo${c}(char *name);
+};
+
+# function ToString:
+               gen qq{
+/**
+ * \\fn char *edg_wll\_${c}ToString($enum code);
+ * \\param code                a $fn numeric code ($enum)
+ * \\return corresponding string (e.g. \"${$f->{codes}}[1]->{name}\")
+ * \\brief converts a $fn numeric code to corresponding string
+ */
+extern char *edg_wll\_${c}ToString($enum code);
+\n};
+       }
+}
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t . '_';
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               if ($f->{codes}) {
+                       my $fn = ucfirst($f->{name});
+                       my $c = "$t${fn}"; # new code
+                       my $enum = "enum edg_wll\_$c"; # new enum name
+
+# enum
+                       gen qq{
+/**
+ * \\enum $enum
+ * $fn codes of the $t event
+ */
+$enum \{
+};
+                       gen $indent."EDG_WLL_${tu}UNDEFINED,\t/**< undefined code */ \n";
+                       for (@{$f->{codes}}) {
+                               gen $indent."EDG_WLL_$tu$_->{name},\t/**< $_->{comment} */ \n";
+                       }
+                       gen "};\n";
+
+# function StringTo:
+                       gen qq{
+/**
+ * \\fn $enum edg_wll_StringTo${c}(char *name);
+ * \\param name                a string representing $t $fn code (e.g. \"${$f->{codes}}[1]->{name}\")
+ * \\return corresponding numeric code ($enum)
+ * \\brief converts a string $t $fn code to corresponding numeric code
+ */
+extern $enum edg_wll_StringTo${c}(char *name);
+};
+
+# function ToString:
+                       gen qq{
+/**
+ * \\fn char *edg_wll\_${c}ToString($enum code);
+ * \\param code                a $t $fn numeric code ($enum)
+ * \\return corresponding string (e.g. \"${$f->{codes}}[1]->{name}\")
+ * \\brief converts a $t $fn numeric code to corresponding string
+ */
+extern char *edg_wll\_${c}ToString($enum code);
+\n};
+               }
+       }
+}
+@@@}
+
+
+/**
+ * common fields of all event types: 
+ */
+
+@@@{
+$indent = "\t";
+gen "#define _EDG_WLL_EVENT_COMMON \\\n";
+gen $indent."edg_wll_EventCode\t\ttype;\\\n";
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       my $fn = $f->{name};
+       my $tn = $f->getType;
+       gen $indent."$tn\t\t$fn;\t\\\n";
+}
+gen "\n";
+@@@}
+
+typedef struct _edg_wll_AnyEvent {
+_EDG_WLL_EVENT_COMMON
+} edg_wll_AnyEvent;
+
+
+/**
+ *  Event types specific structures: 
+ */
+@@@{
+$indent = "\t";
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $s = "edg_wll_${t}Event";
+# typedef struct
+       gen qq{
+/**
+* \\typedef $s
+* structure definition for $t event 
+*/
+typedef struct \_$s \{
+_EDG_WLL_EVENT_COMMON
+};
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $fnu = ucfirst $fn;
+               my $c = $f->{comment};
+               my $tn;
+               if ($f->{codes}) {
+                       $tn = "enum edg_wll\_$t${fnu}";
+               } else {
+                       $tn = $f->getType;
+               }
+               gen $indent."$tn\t$fn;\t/**< $c */\n";
+       }
+       gen "\} $s;\n";
+}
+@@@}
+
+#undef _EDG_WLL_EVENT_COMMON
+
+
+/**
+ * \union edg_wll_Event
+ * \brief All event types union
+ */
+typedef union _edg_wll_Event {
+        edg_wll_EventCode           type; /* it is probably never used */
+        edg_wll_AnyEvent            any;
+@@@{
+$indent = "\t";
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tl = lcfirst $t;
+       gen $indent."edg_wll_${t}Event\t${tl};\n";
+}
+@@@}
+} edg_wll_Event;
+
+
+/**
+ * Initialise an event structure
+ * \return pointer to initialised event structure
+ */
+extern edg_wll_Event *edg_wll_InitEvent(edg_wll_EventCode eventcode);
+
+
+/**
+ * Free the contents of event structure
+ * \param IN event structure to be freed
+ * \warning As event structures are likely to be allocated in arrays,
+ *      the structure itself is not freed.
+ *      Its the responsibility of the caller to call free(event)
+ *      if appropriate.
+ */
+void edg_wll_FreeEvent(
+       edg_wll_Event * event
+);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_EVENTS_H__ */
diff --git a/org.glite.lb.client-interface/interface/jobstat.h.T b/org.glite.lb.client-interface/interface/jobstat.h.T
new file mode 100644 (file)
index 0000000..129b61d
--- /dev/null
@@ -0,0 +1,141 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_JOBSTAT_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_JOBSTAT_H__
+
+/* 
+@@@AUTO
+*/
+
+/*!
+ * \file client/jobstat.h  
+ * \brief edg_wll_JobStat definition and related stuff
+ */
+
+#ident "$Header$"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * Miscelaneous job status numeric codes
+ */
+
+@@@{
+       for my $n ($status->getAllFieldsOrdered) {
+# XXX: we've got only _common_ in jobStatus, no clash possible
+               my $f = selectField $status $n;
+               if ($f->{codes}) {
+                       my $n = ucfirst getName $f;
+                       gen qq{
+!enum edg_wll_Stat$n \{
+};
+                       for (@{$f->{codes}}) {
+                               my $uc = uc $_->{name};
+                               gen qq{
+!      EDG_WLL_STAT_$uc,       /**< $_->{comment} */
+};
+                       }
+                       gen qq{
+!\};
+};
+               }
+       }
+@@@}
+
+
+/*!
+ *
+ * Job status numeric code
+ */
+
+typedef enum _edg_wll_JobStatCode {
+/** Indicates invalid edg_wll_JobStat structure */
+       EDG_WLL_JOB_UNDEF = 0,
+@@@{
+       for my $stat ($status->getTypesOrdered) {
+               my $u = uc $stat;
+               my $c = getTypeComment $status $stat;
+               gen qq{
+!      EDG_WLL_JOB_$u, /**< $c */
+};
+       }
+@@@}
+       EDG_WLL_NUMBER_OF_STATCODES /**< Number of meaningful status codes */
+} edg_wll_JobStatCode;
+
+/*!
+ *
+ * Pair tag = value.
+ */
+typedef struct _edg_wll_TagValue {
+        char *  tag;    /**< User-specified information tag */
+        char *  value;  /**< Value assigned to user-specified information tag */
+} edg_wll_TagValue;
+
+
+/*!
+ *
+ * Description of the job status.
+ * Returned by the edg_wll_JobStatus() function
+ */
+typedef struct _edg_wll_JobStat {
+       edg_wll_JobStatCode     state;          /**< status code */
+@@@{
+       for my $n (getAllFieldsOrdered $status) {
+               selectField $status $n;
+               my $f = getField $status;
+               my $type = getType $f;
+               my $name = getName $f;
+               my $fucname = ucfirst $name;
+               $type = "enum edg_wll_Stat$fucname" if $f->{codes};
+               my $comment = getComment $f;
+               gen qq{
+!      $type   $name;  /**< $comment */
+};
+       }
+@@@}
+
+} edg_wll_JobStat;
+
+/**
+ * \name edg_wll_JobStat manipulation
+ */
+
+/*@{*/
+
+/**
+ * Initialize empty status structure.
+ * Fills in the stucture with NULL's or values with no meaning
+ */
+extern int edg_wll_InitStatus(edg_wll_JobStat *);
+
+/**
+ * Initialize dest structure and copy source status to this destination
+ */
+extern edg_wll_JobStat *edg_wll_CpyStatus(const edg_wll_JobStat *,edg_wll_JobStat *);
+
+/** 
+ * Free status structure contents.
+ * \warning The structure itself is not freed.
+ */
+extern void edg_wll_FreeStatus(edg_wll_JobStat *);
+
+/**
+ * Convert string job status to numeric code.
+ */
+extern edg_wll_JobStatCode edg_wll_StringToStat(const char *);
+
+/** 
+ * Convert numeric job status code to string representation
+ */
+extern char *edg_wll_StatToString(edg_wll_JobStatCode);
+
+/*@}*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_JOBSTAT_H__ */
diff --git a/org.glite.lb.client-interface/interface/load.h b/org.glite.lb.client-interface/interface/load.h
new file mode 100644 (file)
index 0000000..5ca8ead
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_LOAD_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_LOAD_H__
+
+#ident "$Header$"
+
+typedef struct {
+       char    *server_file;
+} edg_wll_LoadRequest;
+
+typedef struct {
+       char    *server_file;
+       time_t  from,to;
+} edg_wll_LoadResult;
+
+/** Load events from a given file into the database
+ * \retval EPERM       operation not permitted
+ * \retval ENOENT      file not found
+ */
+
+int edg_wll_LoadEvents(
+       edg_wll_Context,
+       const edg_wll_LoadRequest *,
+       edg_wll_LoadResult *
+);
+
+#endif
+
diff --git a/org.glite.lb.client-interface/interface/notification.h b/org.glite.lb.client-interface/interface/notification.h
new file mode 100644 (file)
index 0000000..b869daf
--- /dev/null
@@ -0,0 +1,157 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_NOTIFICATION_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_NOTIFICATION_H__
+
+#ident "$Header$"
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/notifid.h"
+#include "glite/lb/context.h"
+#include "glite/lb/consumer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * default and maximal notif timeout (in seconds)
+ */
+#define EDG_WLL_NOTIF_TIMEOUT_DEFAULT   120
+#define EDG_WLL_NOTIF_TIMEOUT_MAX       1800
+
+
+/** Register for receiving notifications.
+ * Connects to the server specified by EDG_WLL_NOTIF_SERVER context parameter
+ * (temporary workaround, should be resolved by registry in future).
+ * \param conditions: the same conditions as for \see edg_wll_QueryJobsExt.
+ *             currently one or more JOBID's are required.
+ *             Only a single occurence of a specific attribute is allowed
+ *             among ANDed conditions (due to the ability to modify them
+ *             further).
+ * \param fd = -1 create or reuse the default listening socket (one per context)
+ *          >= 0 non-default listening socket 
+ * \param address_override if not NULL, use this address instead of extracting it
+ *             from the connection (useful when multiple interfaces are present,
+ *             circumventing NAT problems etc.)
+ * \param valid until when the registration is valid (NULL means no interest in
+ *             the value
+ * \retval 0 OK
+ * \retval EINVAL restrictions on conditions are not met
+ * 
+ */
+int edg_wll_NotifNew(
+       edg_wll_Context         context,
+       edg_wll_QueryRec        const * const *conditions,
+       int                     fd,
+       const char              *address_override,
+       edg_wll_NotifId         *id_out,
+       time_t                  *valid
+);
+
+
+/** Change the receiving local address.
+ * Report the new address to the server.
+ *
+ * \param fd 
+ * \param address_override 
+ * \param valid all same as for \see edg_wll_NotifNew
+ */
+
+int edg_wll_NotifBind(
+       edg_wll_Context         context,
+       const edg_wll_NotifId   id,
+       int                     fd,
+       const char              *address_override,
+       time_t                  *valid
+);
+
+typedef enum _edg_wll_NotifChangeOp {
+       EDG_WLL_NOTIF_NOOP = 0,
+       EDG_WLL_NOTIF_REPLACE,
+       EDG_WLL_NOTIF_ADD,
+       EDG_WLL_NOTIF_REMOVE
+/*      if adding new attribute, add conversion string to common/xml_conversions.c too !! */
+} edg_wll_NotifChangeOp;
+
+/** Modify the query conditions for this notification.
+ *
+ * If op is either EDG_WLL_NOTIF_ADD or EDG_WLL_NOTIF_REMOVE, for the sake
+ * of uniqueness the original conditions must have contained only a single
+ * OR-ed row of conditions on the attributes infolved in the change.
+ * 
+ * \param op action to be taken on existing conditions,
+ *     \see edg_wll_NotifChangeOp
+ */
+int edg_wll_NotifChange(
+       edg_wll_Context         context,
+       const edg_wll_NotifId   id,
+       edg_wll_QueryRec        const * const * conditions,
+       edg_wll_NotifChangeOp   op
+);
+
+/** Refresh the registration, i.e. extend its validity period.
+ * \param valid until when the registration is valid (NULL means no interest in
+ *             the value
+ */
+
+int edg_wll_NotifRefresh(
+       edg_wll_Context         context,
+       const edg_wll_NotifId   id,
+       time_t                  *valid
+);
+
+/** Drop the registration.
+ * Server is instructed not to send notifications anymore, pending ones
+ * are discarded, listening socket is closed, and allocated memory freed.
+ */
+
+int edg_wll_NotifDrop(
+       edg_wll_Context         context,
+       edg_wll_NotifId         *id
+);
+
+/** Receive notification.
+ * The first incoming notification is returned.
+ * \param fd receive on this socket (-1 means the default for the context)
+ * \param timeout wait atmost this time long. (0,0) means polling, NULL waiting
+ *     indefinitely
+ *     
+ * \retval 0 notification received, state_out contains the current job state
+ * \retval EAGAIN no notification available, timeout occured
+ */
+
+int edg_wll_NotifReceive(
+       edg_wll_Context         context,
+       int                     fd,
+       const struct timeval    *timeout,
+       edg_wll_JobStat         *state_out,
+       edg_wll_NotifId         *id_out
+);
+
+
+/** Default socket descriptor where to select(2) for notifications.
+ * Even if nothing is available for reading freom the socket, 
+ * there may be some data cached so calling \see edg_wll_NotifReceive
+ * may return notifications immediately.
+ *
+ * \retval >=0 socket descriptor
+ * \retval -1 error, details set in context
+ */
+
+int edg_wll_NotifGetFd(
+       edg_wll_Context         context
+);
+
+/** Close the default local listening socket.
+ * Useful to force following \see edg_wll_NotifBind to open
+ * a new one.
+ */
+
+int edg_wll_NotifCloseFd(
+       edg_wll_Context         context
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/org.glite.lb.client-interface/interface/notifid.h b/org.glite.lb.client-interface/interface/notifid.h
new file mode 100644 (file)
index 0000000..3d1ed53
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef __GLITE_LB_NOTIFID_H__
+#define __GLITE_LB_NOTIFID_H__
+typedef void   *edg_wll_NotifId;
+#endif
diff --git a/org.glite.lb.client-interface/interface/producer.h.T b/org.glite.lb.client-interface/interface/producer.h.T
new file mode 100644 (file)
index 0000000..b00b7d9
--- /dev/null
@@ -0,0 +1,385 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_PRODUCER_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_PRODUCER_H__
+
+/**
+ * \file edg/workload/logging/client/producer.h
+ * \brief client API for storing data into L&B service
+ */
+
+#ident "$Header$"
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "glite/lb/context.h"
+#include "glite/lb/events.h"
+
+/* Event sources: */
+
+#if 0 /* obsolete */
+#define EDG_WLL_SOURCE_UI           "UserInterface"
+#define EDG_WLL_SOURCE_RB           "ResourceBroker"
+#define EDG_WLL_SOURCE_JSS          "JobSubmissionService"  /* aka Condor-G */
+#define EDG_WLL_SOURCE_JOBMGR       "GlobusJobmanager"
+#define EDG_WLL_SOURCE_LRMS         "LocalResourceManager"
+#define EDG_WLL_SOURCE_APP          "Application"
+
+#define EDG_WLL_SOURCE_NS      "NetworkServer"
+#define EDG_WLL_SOURCE_WM      "WorkloadManager"
+#define EDG_WLL_SOURCE_BH      "BigHelper"
+#define EDG_WLL_SOURCE_LM      "LogMonitor"
+
+#endif
+
+/* Event formats: */
+
+#define EDG_WLL_FORMAT_COMMON  "DATE=%s HOST=\"%|Us\" PROG=edg-wms LVL=%s DG.PRIORITY=%d DG.SOURCE=\"%|Us\" DG.SRC_INSTANCE=\"%|Us\" DG.EVNT=\"%s\" DG.JOBID=\"%s\" DG.SEQCODE=\"%|Us\" "
+#define EDG_WLL_FORMAT_USER    "DG.USER=\"%|Us\" "
+@@@{
+# FIXME:
+# this is all functional, but doesn't fit to all common fields (eg. USER)
+#
+#gen "#define EDG_WLL_FORMAT_COMMON\t\"";
+#selectType $event '_common_';
+#for ($event->getFieldsOrdered) {
+#      my $f = selectField $event $_;
+#      my $fn = getName $f 'ULM';
+#      my $fnu = uc $fn;
+#      if (hasAlias $f 'ULM') {
+#              gen "$fnu=%s ";
+#      } else {
+#              gen "DG\.$fnu=";
+#                      gen $f->toFormatString;
+#                      gen " ";
+#      }
+#}
+#gen "\"\n";
+#
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       gen "#define EDG_WLL_FORMAT_$tu\t\"";
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $fnu = uc $fn;
+               gen "DG.$tu.$fnu=\\\"";
+               if ($f->{codes}) {
+                       gen "%s";
+               } else {
+                       gen $f->toFormatString;
+               }
+               gen "\\\" ";
+       }
+       gen "\"\n";
+}
+@@@}
+#define EDG_WLL_FORMAT_NOTIFICATION_COMMON     "DATE=%s HOST=\"%|Us\" PROG=edg-wms LVL=%s DG.SOURCE=\"%|Us\" DG.SRC_INSTANCE=\"%|Us\" DG.TYPE=\"notification\" "
+#define EDG_WLL_FORMAT_SYSCMPSTAT      "DG.SCHED.STATUS=\"%|Us\" "
+#define EDG_WLL_FORMAT_SYSCLSTAT       "DG.SCHED.NODE=\"%|Us\" DG.SCHED.STATUS=\"%|Us\" "
+
+
+/* edg_wll_LogEvent shortcuts */
+@@@{
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $a = "(edg_wll_Context context";
+       my $b = "(context,EDG_WLL_EVENT_$tu,EDG_WLL_FORMAT_$tu";
+       my $doc = qq{
+ * \\param context\tcontext to work with,
+};
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->getName;
+               my $ft;
+                if ($f->{codes}) {
+#                        $ft = "enum edg_wll\_$t" . ucfirst $fn;
+                        $ft = "char *";
+                } else {
+                        $ft = $f->getType;
+                }
+               $ft = "const ".$ft;
+               my $fc = $f->getComment;
+               $a = $a . ", $ft $fn";
+               $b = $b . ", $fn";
+               $doc = $doc . " * \\param $fn\t$fc\n";
+       }
+       $a = $a . ")";
+       $b = $b . ")";
+       gen qq{
+/**
+ * \\fn int edg_wll_Log$t$a; 
+ * \\brief simple wrapper around edg_wll_LogEvent for event $t} . $doc . qq{ * \\see edg_wll_LogEvent\(\)
+ */
+};
+       gen "\nextern int edg_wll_Log$t$a;\n\n";
+#      gen qq{
+#int edg_wll_Log$t$a
+#\{
+#      return edg_wll_LogEvent$b;
+#\}\n
+#};
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->getName;
+               my $ft;
+                if ($f->{codes}) {
+#                        $ft = "enum edg_wll\_$t" . ucfirst $fn;
+                        $ft = "char *";
+                } else {
+                        $ft = $f->getType;
+                }
+               my $ftreg = $ft;
+               $ftreg =~ s/\*/\\\*/g;
+               $ftreg = "const ".$ftreg;
+               my $fc = $f->getComment;
+               if ($f->{codes}) {
+                       for (@{$f->{codes}}) {
+                               my $code = uc $_->{name};
+                               my $c = $a;
+                               my $d = $b;
+                               my $e = $doc;
+                               $c =~ s/, $ftreg $fn//g;
+                               $d =~ s/$fn/"$code"/g;
+                               $e =~ s/ \* \\param $fn\t$fc\n//g;
+                               gen qq{
+/**
+ * \\fn int edg_wll_Log$t$code$c; 
+ * \\brief simple wrapper around edg_wll_LogEvent for event $t, $fn $code} . $e . qq{ * \\see edg_wll_LogEvent\(\)
+ */
+};
+                               gen "\nextern int edg_wll_Log$t$code$c;\n\n";
+#                              gen qq{
+#int edg_wll_Log$t$code$c
+#\{
+#      return edg_wll_LogEvent$d;
+#\}\n
+#};
+                       }
+               }
+       }
+}
+@@@}
+
+
+/**
+ * Formats a logging message and sends it asynchronously to local-logger
+ * \brief generic asynchronous logging function
+ * \param context      INOUT context to work with,
+ * \param event                IN type of the event,
+ * \param fmt          IN printf()-like format string,
+ * \param ...          IN event specific values/data according to fmt,
+ * \retval 0           successful completition,
+ * \retval EINVAL      bad jobId, unknown event code, or the format string together with the remaining arguments does not form a valid event,
+ * \retval ENOSPC      L&B infrastructure failed to accept the event due to lack of disk space etc.,
+ * \retval ENOMEM      failed to allocate memory,
+ * \retval ECONNREFUSED        cannot connect to the specified local logger,
+ * \retval EAGAIN      non blocking return from the call, the event may or may not get logged,
+ * \retval EDG_WLL_ERROR_NOJOBID logging call attempted without assigning jobId to the context.
+ */
+extern int edg_wll_LogEvent(
+       edg_wll_Context context,
+       edg_wll_EventCode event,
+       char *fmt, ...);
+
+
+/**
+ * Formats a logging message and sends it synchronously to local-logger
+ * \brief generic synchronous logging function
+ * \param context      INOUT context to work with,
+ * \param event                IN type of the event,
+ * \param fmt          IN printf()-like format string,
+ * \param ...          IN event specific values/data according to fmt,
+ * \retval 0           successful completition,
+ * \retval EINVAL      bad jobId, unknown event code, or the format string together with the remaining arguments does not form a valid event,
+ * \retval ENOSPC      L&B infrastructure failed to accept the event due to lack of disk space etc.,
+ * \retval ENOMEM      failed to allocate memory,
+ * \retval ECONNREFUSED        cannot connect to the specified local logger,
+ * \retval EAGAIN      non blocking return from the call, the event may or may not get logged,
+ * \retval EDG_WLL_ERROR_NOJOBID logging call attempted without assigning jobId to the context,
+ * \retval EPERM       the user is not authorized to add events to this job,
+ * \retval EDG_WLL_ERROR_DB_DUP_KEY exactly the same event has been already stored.
+ */
+extern int edg_wll_LogEventSync(
+       edg_wll_Context context,
+       edg_wll_EventCode event,
+       char *fmt, ...);
+
+
+/**
+ * Instructs interlogger to to deliver all pending events related to current job
+ * \brief flush events from interlogger
+ * \note sort of status query more than a command
+ * \param context      INOUT context to work with,
+ * \param timeout      INOUT wait at most this much time for completition, remaining time on return,
+ * \retval 0           successful completition,
+ * \retval EDG_WLL_ERROR_INTERLOG_TIMEOUT the inter-logger did not respond within the timeout,
+ * \retval EDG_WLL_ERROR_INTERLOG_CONLOST inter-logger lost connection to one or more servers,
+ * \retval EDG_WLL_ERROR_INTERLOG_AGAIN   not all pending events were delivered within the timeout.
+ */
+extern int edg_wll_LogFlush(
+       edg_wll_Context context,
+       struct timeval *timeout);
+
+
+/**
+ * Instructs interlogger to to deliver all pending events
+ * \brief flush all events from interlogger
+ * \note same as edg_wll_LogFlush() for all jobs known to interlogger
+ * \see edg_wll_LogFlush()
+ */
+extern int edg_wll_LogFlushAll(
+       edg_wll_Context context,
+       struct timeval *timeout);
+
+/**
+ * Set a current job for given context.
+ * \note Should be called before any logging call.
+ * \param context INOUT context to work with
+ * \param job IN further logging calls are related to this job
+ * \param code IN sequence code as obtained from previous component
+ * \param flags IN flags on code handling (\see API documentation)
+ */
+extern int edg_wll_SetLoggingJob(
+       edg_wll_Context context,
+       const edg_wlc_JobId     job,
+       const char *            code,
+       int                     flags
+);
+
+
+/**
+ * Register job with L&B service.
+ * Done via logging REGJOB event, may generate subjob id's and create
+ * the parent-children associations.
+ * Set the job as current for the context and initialize sequence code.
+ *
+ * Partitionable jobs should set num_subjobs=0 initially,
+ * and re-register when number of subjobs becomes known.
+ *
+ * \param type EDG_WLL_JOB_SIMPLE,  EDG_WLL_JOB_DAG, or EDG_WLL_JOB_PARTITIONABLE
+ * \param jdl user-specified JDL
+ * \param ns network server contact
+ * \param num_subjobs number of subjobs to create
+ * \param seed seed used for subjob id's generator.
+ *     Use non-NULL value to be able to regenerate the set of jobid's
+ * \param subjobs returned subjob id's
+ */
+
+/* backward compatibility */
+#define EDG_WLL_JOB_SIMPLE     EDG_WLL_REGJOB_SIMPLE
+extern int edg_wll_RegisterJob(
+       edg_wll_Context         context,
+       const edg_wlc_JobId     job,
+       enum edg_wll_RegJobJobtype      type,
+       const char *            jdl,
+       const char *            ns,
+       int                     num_subjobs,
+       const char *            seed,
+       edg_wlc_JobId **        subjobs
+);
+
+/** 
+ * Synchronous variant of edg_wll_RegisterJob
+ */
+
+extern int edg_wll_RegisterJobSync(
+       edg_wll_Context         context,
+       const edg_wlc_JobId     job,
+       enum edg_wll_RegJobJobtype      type,
+       const char *            jdl,
+       const char *            ns,
+       int                     num_subjobs,
+       const char *            seed,
+       edg_wlc_JobId **        subjobs
+);
+
+/**
+ * Register subjobs in a batch.
+ * Mainly used to provide JDL's of individual subjobs in a more efficient
+ * way than logging them one by one.
+ * \param jdls array of JDL's
+ * \param subjobs      array of jobid's in the same order
+ */
+
+extern int edg_wll_RegisterSubjobs(
+       edg_wll_Context         context,
+       const edg_wlc_JobId     parent,
+       char const * const *    jdls,
+       const char *            ns,
+       edg_wlc_JobId const *   subjobs
+);
+
+
+/**
+ * Generate or regenerate set of subjob ID's.
+ * Calls the same algorithm used to generate subjob ID's in edg_wll_RegisterJob().
+ * Local semantics only, server is not contacted.
+ */
+
+extern int edg_wll_GenerateSubjobIds(
+       edg_wll_Context         context,
+       const edg_wlc_JobId     parent,
+       int                     num_subjobs,
+       const char *            seed,
+       edg_wlc_JobId **        subjobs
+);
+
+
+enum edg_wll_Permission {
+       EDG_WLL_PERM_READ  = 1,
+       EDG_WLL_PERM_WRITE = 4,
+       EDG_WLL_PERM_ADMIN = 8,
+};
+
+enum edg_wll_PermissionType {
+       EDG_WLL_PERM_ALLOW,
+       EDG_WLL_PERM_DENY,
+};
+
+enum edg_wll_ACLOperation {
+       EDG_WLL_ACL_ADD,
+       EDG_WLL_ACL_REMOVE,
+};
+
+enum edg_wll_UserIdType {
+       EDG_WLL_USER_SUBJECT,           /* X.509 subject name */
+       EDG_WLL_USER_VOMS_GROUP,        /* VOMS group membership */
+};
+
+/**
+ * Change ACL for given job.
+ * \param specification of user's credential
+ * \param user_id_type type of user_id,
+ *    for EDG_WLL_USER_SUBJECT the user_id parameter is expected to be user's subject name
+ *    for EDG_WLL_USER_VOMS_GROUP the user_id is expected to be of the form VO:group specifying required group membersip as managed by VOMS
+ * \param permission ACL permission to change
+ * \param permission_type type of given permission (allow or deny operation) 
+ * \param operation operation to perform with ACL (add or remove record)
+ */
+extern int edg_wll_ChangeACL(
+       edg_wll_Context         context,
+       const edg_wlc_JobId     job,
+       const char *            user_id,
+       enum edg_wll_UserIdType user_id_type,
+       enum edg_wll_Permission         permission,
+       enum edg_wll_PermissionType     permission_type,
+       enum edg_wll_ACLOperation       operation
+);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_PRODUCER_H__ */
diff --git a/org.glite.lb.client-interface/interface/purge.h b/org.glite.lb.client-interface/interface/purge.h
new file mode 100644 (file)
index 0000000..531c903
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_PURGE_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_PURGE_H__
+
+#ident "$Header$"
+
+/** Purge or dump request */
+typedef struct _edg_wll_PurgeRequest {
+       char    **jobs;         /**< list of jobid's to work on */ 
+
+/** Purge jobs that are in the given states and "untouched" at least for the
+  * specified interval.
+  * Currently applicable for CLEARED, ABORTED, CANCELLED and OTHER (catchall).
+  * The other array members are for future extensions.
+  * Negative values stand for server defaults.
+  */
+       time_t  timeout[EDG_WLL_NUMBER_OF_STATCODES];
+#define EDG_WLL_PURGE_JOBSTAT_OTHER    EDG_WLL_JOB_UNDEF
+
+
+/**
+ * Actions to be taken and information required.
+ */
+       int     flags;
+
+/** no dry run */
+#define EDG_WLL_PURGE_REALLY_PURGE     1
+/** return list of jobid matching the purge/dump criteria */
+#define EDG_WLL_PURGE_LIST_JOBS                2
+/** dump to a file on the sever */
+#define EDG_WLL_PURGE_SERVER_DUMP      4
+/** TODO: stream the dump info to the client */
+#define EDG_WLL_PURGE_CLIENT_DUMP      8
+/* ! when addning new constant, add it also to common/xml_conversions.c ! */
+
+       
+/** private request processing data (for the reentrant functions) */
+/* TODO */
+       
+} edg_wll_PurgeRequest;
+
+/** Output data of a purge or dump */
+typedef struct _edg_wll_PurgeResult {
+       char    *server_file;   /**< filename of the dump at the server */
+       char    **jobs;         /**< affected jobs */
+/* TODO: output of the streaming interface */
+} edg_wll_PurgeResult;
+
+
+/** Client side purge/dump 
+ * \retval EAGAIN only partial result returned, call repeatedly to get all
+ *     output data
+ */
+int edg_wll_Purge(
+       edg_wll_Context ctx,
+       edg_wll_PurgeRequest *request,
+       edg_wll_PurgeResult     *result
+);
+
+#endif
diff --git a/org.glite.lb.client-interface/project/properties.xml b/org.glite.lb.client-interface/project/properties.xml
new file mode 100755 (executable)
index 0000000..a58c826
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB Common component common properties">
+
+       <property file="build.properties" />    
+       <property name="subsystem.name" value="${lb.subsystem.name}"/>
+       <property name="subsystem.prefix" value="${lb.subsystem.prefix}"/>
+       <property name="component.prefix" value="client-iface" />
+
+       <import file="${component.general.properties.file}" />
+
+</project>
diff --git a/org.glite.lb.client/.Makefile.swp b/org.glite.lb.client/.Makefile.swp
new file mode 100644 (file)
index 0000000..d54e7cd
Binary files /dev/null and b/org.glite.lb.client/.Makefile.swp differ
diff --git a/org.glite.lb.client/Makefile b/org.glite.lb.client/Makefile
new file mode 100644 (file)
index 0000000..dd811ad
--- /dev/null
@@ -0,0 +1,91 @@
+include Makefile.inc
+
+
+VPATH:=${src} 
+AT3:=perl -I${lbconfig} ${lbproject}/at3
+
+SUFFIXES = .T 
+
+DEBUG:=-g -O0 
+CFLAGS:=${DEBUG} \
+       -I${stageinc} -I${src} -I${interface}\
+       -I${repository}/${globus}/include/${globusflavour} \
+        -I${repository}/${globus}/include/${globusflavour}/openssl \
+       -I${repository}/${expat}/include
+
+CXXFLAGS:=${CFLAGS}
+LDFLAGS:=-L${stagelib}
+
+COMPILE:=libtool --mode=compile ${CC} ${CFLAGS}
+LINK:=libtool --mode=link ${CC} ${LDFLAGS} 
+INSTALL:=libtool --mode=install install
+
+GLOBUS_LIBS:= -L${repository}/${globus}/lib \
+       -lglobus_common_${globusflavour} \
+       -lssl_${globusflavour}
+
+EXT_LIBS:= ${GLOBUS_LIBS} \
+       -L${repository}/${ares}/lib -lares \
+       -L${repository}/${expat}/lib -lexpat \
+
+LIBOBJS:=connection.o consumer.o notification.o prod_proto.o \
+       producer.o uiwrap.o
+
+PLUSOBJS:=Event.o Job.o JobStatus.o Notification.o ServerConnection.o
+PUB_HDRS:=CountRef.h Event.h JobJobStatus.h Notification.h ServerConnection.h \
+       LoggingExceptions.h
+
+LIBLOBJS:=`echo ${LIBOBJS} | sed 's/\.o/\.lo/g'`
+PLUSLOBJS:=`echo ${PLUSOBJS} | sed 's/\.o/\.lo/g'`
+HELPERS:=-L${stagelib} -lglite_wms_tls_ssl_helpers
+
+LIB:=libglite_lb_client.la
+PLUSLIB:=libglite_lb_clientpp.la
+
+TOOLS:=dump load purge
+
+default: all
+
+compile: ${LIB} ${PLUSLIB} ${TOOLS} logevent
+
+check:
+       echo No unit tests so far.
+
+${LIB}: ${LIBOBJS}
+       ${LINK} -o $@ ${LIBLOBJS} -rpath ${stagelib} -lglite_lb_common ${HELPERS}
+# ${EXT_LIBS}
+
+${PLUSLIB}: ${PLUSOBJS}
+       ${LINK} -o $@ ${PLUSLOBJS} -rpath ${stagelib} ${LIB}
+
+stage export all: compile
+       ${INSTALL} -m 644 ${LIB} ${stagelib}
+       for p in ${TOOLS} logevent; do \
+               ${INSTALL} -m 755 "$$p" "${stagebin}/glite-lb-$$p"; \
+       done
+
+logevent: logevent.o args.o
+       ${LINK} -o $@ logevent.o args.o ${LIB} ${EXT_LIBS}
+
+${TOOLS}: %: %.o
+       ${LINK} -o $@ $< ${LIB} ${EXT_LIBS}
+
+${TOOLS}: ${LIB}
+
+${LIBOBJS}: %.o: %.c
+       ${COMPILE} -c $<
+
+%.o: %.c 
+       ${CC} ${CFLAGS} -c $<
+
+%.c: %.c.T
+       rm -f $@
+       ${AT3} $< >$@ || rm -f $@
+       chmod -w $@ >/dev/null
+
+%.cpp: %.cpp.T
+       rm -f $@
+       ${AT3} $< >$@ || rm -f $@
+       chmod -w $@ >/dev/null
+
+
diff --git a/org.glite.lb.client/build.xml b/org.glite.lb.client/build.xml
new file mode 100755 (executable)
index 0000000..4aa4105
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="lb" default="dist">
+       
+       <import file="../org.glite/project/baseline.properties.xml" />
+       <import file="./project/properties.xml"/>
+       <import file="${subsystem.properties.file}"/>
+       <import file="${global.properties.file}" />
+
+       <property file="${user.dependencies.file}"/>
+       <property file="${component.dependencies.file}" />
+       <property file="${subsystem.dependencies.file}" />
+       <property file="${global.dependencies.file}"/>
+       
+       <import file="${subsystem.taskdefs.file}" />
+       <import file="${global.taskdefs.file}" />
+
+       <import file="${global.targets-external-dependencies.file}"/>   
+       <import file="${global.targets-make.file}" />
+               
+       <property file="${module.version.file}"/>
+
+       <target name="localinit"
+               description="Module specific initialization tasks">
+               <antcall target="lbmakefiles"/>
+       </target>
+
+       <target name="localcompile"
+               description="Module specific compile tasks">
+       </target>
+       
+       <target name="localclean"
+               description="Module specific cleaning tasks">
+       </target>
+       
+</project>             
diff --git a/org.glite.lb.client/interface/CountRef.h b/org.glite.lb.client/interface/CountRef.h
new file mode 100644 (file)
index 0000000..cedf203
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_COUNTREF_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_COUNTREF_HPP__
+
+#define EWL_BEGIN_NAMESPACE namespace edg { namespace workload { namespace logging { namespace client {
+#define EWL_END_NAMESPACE } } } }
+
+EWL_BEGIN_NAMESPACE;
+
+template<typename T>
+class CountRef {
+public:
+       CountRef(void *);
+//     CountRef(void *,void (*)(void *));
+
+       void use(void);
+       void release(void);
+
+       void    *ptr;
+private:
+       int     count;
+//     void    (*destroy)(void *);
+};
+
+template <typename T>
+CountRef<T>::CountRef(void *p)
+{
+       ptr = p;
+       count = 1;
+}
+
+template <typename T>
+void CountRef<T>::release(void)
+{
+       if (--count == 0) {
+               T::destroyFlesh(ptr);
+               delete this;
+       }
+}
+
+template <typename T>
+void CountRef<T>::use(void)
+{
+       count++;
+}
+
+EWL_END_NAMESPACE;
+
+#endif
diff --git a/org.glite.lb.client/interface/Event.h.T b/org.glite.lb.client/interface/Event.h.T
new file mode 100644 (file)
index 0000000..7aac0b4
--- /dev/null
@@ -0,0 +1,147 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_EVENT_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_EVENT_HPP__
+
+#include "edg/workload/logging/client/CountRef.h"
+#include "edg/workload/common/jobid/JobId.h"
+
+#include <utility>
+#include <vector>
+#include <string>
+
+#ident "$Header$"
+
+/** @file Event.h
+ *  @version $Revision$
+ */
+
+/*
+@@@AUTO
+*/
+@@@LANG: C++
+
+#include "edg/workload/logging/client/events.h"
+#include "edg/workload/logging/common/notifid.h"
+
+EWL_BEGIN_NAMESPACE;
+
+class Event {
+       friend class Job;
+       friend class ServerConnection;
+       friend class CountRef<Event>;
+public:
+       /** Event type codes.
+        * Identify which of the event fields are valid.
+        */
+
+       enum Type {
+               UNDEF = 0,
+@@@{
+       for my $e ($event->getTypesOrdered) {
+               my $u = uc $e;
+               my $c = getTypeComment $event $e;
+               gen "\t\t$u,\t/**< $c */\n";
+       }
+@@@}
+               TYPE_MAX
+       };
+
+       /** Event attribute symbolic identifier. */
+       enum Attr {
+@@@{
+       for (sort {$a cmp $b} getAllFields $event) {
+               my $u = $_;
+# $u =~ s/([a-z])([A-Z])/$1_$2/g;
+               $u = uc $u;
+
+               my $c = "\t/**\n";
+               for my $t (sort $event->getFieldOccurence($_)) {
+                       selectType $event $t;
+                       my $cc = getFieldComment $event $_;
+                       $t = 'common' if $t eq '_common_';
+                       $c .= "\t * $t: $cc\n";
+               }
+               $c .= "\t */\n";
+
+               gen "$c\t\t$u,\n";
+       }
+@@@}
+               ATTR_MAX
+       };
+
+@@@{
+       for my $f (getAllFields $event) {
+               for my $t (getFieldOccurence $event $f) {
+                       my $ff;
+                       my $ut;
+                       my $utf;
+                       if ($t eq '_common_') {
+                               $ff = $f;
+                               $ut = '';
+                               $utf = '';
+                       }
+                       else {
+                               selectType $event $t;
+                               selectField $event $f;
+                               $ff = getField $event;
+                               $ut = uc $t . '_';
+                               $utf = ucfirst $t;
+                       }
+                       if ($ff->{codes}) {
+                               gen qq{
+!      enum ${utf}Code \{
+};
+                               for (@{$ff->{codes}}) {
+                                       gen qq{
+!              $ut$_->{name},  /**< $_->{comment} */
+};
+                               }
+                               gen qq{
+!      \};
+};
+                       }
+               }
+       }
+@@@}
+
+       enum AttrType { INT_T, STRING_T, TIMEVAL_T, PORT_T, LOGSRC_T, JOBID_T, NOTIFID_T };
+
+       Type    type;
+
+       Event(void);
+        Event(edg_wll_Event *);
+       Event(const Event &);
+       ~Event(void);
+
+
+       /** Assign new Event to an existing instance. */
+       Event & operator= (const Event &);
+
+       /** String representation of the event type */
+       const std::string & name(void) const;
+
+       /** Retrieve integer attribute */
+       int     getValInt(Attr) const;
+
+       /** Retrieve string attribute */
+       std::string getValString(Attr) const;
+
+        /** Retrieve time attribute */
+        struct timeval getValTime(Attr) const;
+               
+        /** Retrieve jobid attribute */
+        const edg::workload::common::jobid::JobId getValJobId(Attr) const;
+
+       /** Attribute name */
+       const std::string & getAttrName(Attr) const;
+
+       /** List of attributes and types valid for this instance */
+       const std::vector<std::pair<Attr,AttrType> >  & getAttrs(void) const;
+
+private:
+       static void     destroyFlesh(void *);
+       CountRef<Event> *flesh;
+};
+
+EWL_END_NAMESPACE;
+
+#endif
diff --git a/org.glite.lb.client/interface/Job.h b/org.glite.lb.client/interface/Job.h
new file mode 100644 (file)
index 0000000..d041dfe
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_JOB_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_JOB_HPP__
+
+#ident "$Header$"
+
+#include "edg/workload/common/jobid/JobId.h"
+#include "edg/workload/logging/client/Event.h"
+#include "edg/workload/logging/client/JobStatus.h"
+#include "edg/workload/logging/client/ServerConnection.h"
+
+
+/**
+ * @file Job.h
+ * @version $Revision$
+ */
+
+EWL_BEGIN_NAMESPACE;
+
+/** L&B job.
+ * Implementation of L&B job-specific calls.
+ * Connection to the server is maintained transparently.
+*/
+  
+class Job {
+public:
+  Job(void);
+  Job(const edg::workload::common::jobid::JobId &);
+  ~Job();
+  
+  /** Assign new JobId to an existing instance.
+   * Connection to server is preserved if possible.
+   */
+  
+  Job & operator= (const edg::workload::common::jobid::JobId &);
+
+/**
+ * Status retrieval bitmasks. Used ORed as Job::status() argument,
+ * determine which status fields are actually retrieved.
+ */
+  static const int STAT_CLASSADS;       /**< various job description fields */
+  static const int STAT_CHILDREN;       /**< list of subjob JobId's */
+  static const int STAT_CHILDSTAT;      /**< apply the flags recursively to subjobs */
+
+  /** Return job status */
+  JobStatus status(int) const;
+  
+  /** Return all events corresponding to this job */
+  void log(std::vector<Event> &) const;
+  const std::vector<Event> log(void) const;
+  
+  /** Return last known address of a listener associated to the job.
+   * \param name name of the listener
+   * \return hostname and port number
+   */
+  const std::pair<std::string,uint16_t> queryListener(const std::string & name) const;
+  
+  /** Manipulate LB parameters, the same as for edg_wll_Context in C */
+  void setParam(edg_wll_ContextParam, int); 
+  void setParam(edg_wll_ContextParam, const std::string); 
+  void setParam(edg_wll_ContextParam, const struct timeval &); 
+
+  int getParamInt(edg_wll_ContextParam) const;
+  std::string getParamString(edg_wll_ContextParam) const;
+  struct timeval getParamTime(edg_wll_ContextParam) const;
+  
+private:
+  ServerConnection     server;
+  edg::workload::common::jobid::JobId                  jobId;
+};
+
+EWL_END_NAMESPACE;
+
+#endif
diff --git a/org.glite.lb.client/interface/JobStatus.h.T b/org.glite.lb.client/interface/JobStatus.h.T
new file mode 100644 (file)
index 0000000..c1519b0
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_JOBSTATUS_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_JOBSTATUS_HPP__
+
+/*
+@@@AUTO
+*/
+
+@@@LANG: C++
+
+#include "edg/workload/logging/client/CountRef.h"
+#include "edg/workload/common/jobid/JobId.h"
+
+#include "edg/workload/common/jobid/jobid.h"
+#include <sys/time.h>
+#include "edg/workload/logging/client/jobstat.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+EWL_BEGIN_NAMESPACE;
+
+/**
+ * Description of job status.
+ * The status is computed from a sequence of logged events
+ */
+
+
+class JobStatus {
+       friend class Job;
+       friend class CountRef<JobStatus>;
+public:
+       enum Code {
+               UNDEF = 0,      /**< indicates invalid, i.e. uninitialized instance */
+@@@{
+       for my $stat ($status->getTypesOrdered)
+       {
+               my $u = uc($stat);
+               my $c = getTypeComment $status $stat;
+               gen qq{
+!              $u,     /**< $c */
+};
+       }
+@@@}
+               CODE_MAX
+       };
+
+       enum Attr {
+@@@{
+       selectType $status '_common_';
+       for my $u (sort {$a cmp $b} getAllFields $status) {
+               selectField $status $u;
+               my $f = getField $status;
+               $u =~ s/([a-z])([A-Z])/$1_$2/g;
+               $u = uc $u;
+
+               gen "\t/** $f->{comment} */\n\t\t$u,\n";
+       }
+@@@}
+               ATTR_MAX
+       };
+
+@@@{
+       selectType $status '_common_';
+       for my $n (getAllFields $status) {
+               selectField $status $n;
+               my $f = getField $status;
+               if ($f->{codes}) {
+                        my $n = uc getName $f;
+                       gen qq{
+!      enum \{
+};
+                       for (@{$f->{codes}}) {
+                               gen qq{
+!              $n\_$_->{name}, /**< $_->{comment} */
+};
+                       }
+                       gen qq{
+!      \};
+};
+               }
+       }
+@@@}
+       enum AttrType { INT_T, 
+                       STRING_T, 
+                       TIMEVAL_T, 
+                       BOOL_T,
+                       JOBID_T,
+                       INTLIST_T, 
+                       STRLIST_T, 
+                       TAGLIST_T, 
+                       STSLIST_T 
+       };
+  
+       /** Numeric status code */
+       Code    status;
+  
+       /** String representation of the status code */
+       const std::string & name(void) const;
+  
+       /** Retrieve integer attribute */
+       int     getValInt(Attr) const;
+  
+       /** Retrieve string attribute */
+       std::string getValString(Attr) const;
+  
+       /** Retrieve time attribute */
+       struct timeval  getValTime(Attr) const;
+  
+       /** Retrieve jobid attribute */
+       const edg::workload::common::jobid::JobId  getValJobId(Attr) const;
+
+       /** Retrieve bool attribute */
+       bool getValBool(Attr) const;
+
+       /** Retrieve int list attribute */
+       const std::vector<int> getValIntList(Attr) const;
+
+       /** Retrieve string list attribute */
+       const std::vector<std::string> getValStringList(Attr) const;
+
+       /** Retrieve tag list attribute */
+       const std::vector<std::pair<std::string,std::string> > getValTagList(Attr) const;
+
+       /** Retrieve job status list attribute */
+       const std::vector<JobStatus> getValJobStatusList(Attr) const;
+
+       /** Attribute name */
+       const std::string& getAttrName(Attr) const;
+  
+       /** List of attributes and types valid for this instance */
+       const std::vector<std::pair<Attr,AttrType> >& getAttrs(void) const;
+  
+       JobStatus(void);
+       JobStatus(const JobStatus &);
+       JobStatus & operator=(const JobStatus &);
+       JobStatus(const edg_wll_JobStat &);
+       JobStatus & operator=(const edg_wll_JobStat&);
+       virtual ~JobStatus();
+
+protected:
+       edg_wll_JobStat *c_ptr(void);
+
+private:
+       static void     destroyFlesh(void *);
+       CountRef<JobStatus> *flesh;
+};
+
+EWL_END_NAMESPACE;
+
+#endif
+
diff --git a/org.glite.lb.client/interface/LoggingExceptions.h b/org.glite.lb.client/interface/LoggingExceptions.h
new file mode 100644 (file)
index 0000000..9f3a2c9
--- /dev/null
@@ -0,0 +1,144 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_LOGGING_EXCEPTIONS_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_LOGGING_EXCEPTIONS_HPP__
+
+#include "edg/workload/common/utilities/Exceptions.h"
+
+#include <pthread.h>
+
+#ident "$Header$"
+
+/** @file LoggingExceptions.h
+ *  @version $Revision$
+ */
+
+EWL_BEGIN_NAMESPACE;
+
+class Exception: public edg::workload::common::utilities::Exception {
+public:
+       
+       /* constructor for mandatory fields */
+       Exception(const std::string& source,
+                 int line_number,
+                 const std::string& method,
+                 int   code,
+                 const std::string& exception) 
+               : edg::workload::common::utilities::Exception(source, 
+                                                             line_number, 
+                                                             method, 
+                                                             code, 
+                                                             "edg::workload::logging::Exception")
+               { error_message = exception; };
+       
+       /* constructor for mandatory fields AND exception chain */
+       Exception(const std::string& source,
+                 int line_number,
+                 const std::string& method,
+                 int   code,
+                 const std::string& exception,
+                 const edg::workload::common::utilities::Exception &exc)
+               : edg::workload::common::utilities::Exception(source, 
+                                                             line_number, 
+                                                             method, 
+                                                             code, 
+                                                             "edg::workload::logging::Exception")
+               { error_message = exception + ": " + exc.what(); };
+};
+
+
+class LoggingException: public Exception {
+public:
+       
+       /* constructor for mandatory fields */
+       LoggingException(const std::string& source,
+                        int line_number,
+                        const std::string& method,
+                        int   code,
+                        const std::string& exception) 
+               : Exception(source, line_number, method, code, exception)
+               {};
+       
+       /* constructor for mandatory fields AND exception chain */
+       LoggingException(const std::string& source,
+                        int line_number,
+                        const std::string& method,
+                        int   code,
+                        const std::string& exception, 
+                        const edg::workload::common::utilities::Exception &exc)
+               : Exception(source, line_number, method, code, exception)
+               {};
+};
+
+
+class OSException: public Exception {
+public:
+       
+       /* constructor for mandatory fields */
+       OSException(const std::string& source,
+                   int line_number,
+                   const std::string& method,
+                   int   code,
+                   const std::string& exception)
+               : Exception(source, 
+                           line_number, 
+                           method, 
+                           code, 
+                           exception + ": " + strerror(code))
+               {};
+       
+       /* constructor for mandatory fields AND exception chain */
+       OSException(const std::string& source,
+                   int line_number,
+                   const std::string& method,
+                   int   code,
+                   const std::string& exception,
+                   const edg::workload::common::utilities::Exception &exc)
+               : Exception(source, 
+                           line_number, 
+                           method, 
+                           code, 
+                           exception + ": " + strerror(code))
+               {};
+};
+
+
+#define EXCEPTION_MANDATORY                           \
+       __FILE__,                                     \
+        __LINE__,                                     \
+        std::string(CLASS_PREFIX) + __FUNCTION__         
+
+#define STACK_ADD                                     
+
+/* note: we can use __LINE__ several times in macro, it is expanded into one row */
+#define throw_exception(context, exception)           \
+{ STACK_ADD;                                          \
+  {                                                   \
+     char *text, *desc;                               \
+     int  code;                                       \
+     std::string exc;                                      \
+                                                      \
+     code = edg_wll_Error((context), &text, &desc);   \
+     exc = exception;                                 \
+     if (text) {                                     \
+       exc += ": ";                                  \
+       exc += text;                                  \
+     }                                               \
+     if (desc) {                                     \
+       exc += ": ";                                  \
+       exc += desc;                                  \
+     }                                               \
+     free(text);                                      \
+     free(desc);                                      \
+     throw LoggingException(EXCEPTION_MANDATORY,      \
+                           code,                     \
+                           exc);                     \
+  }                                                  \
+}
+#define check_result(code, context, desc)             \
+  if((code)) throw_exception((context), desc)
+
+
+
+EWL_END_NAMESPACE;
+
+#endif
diff --git a/org.glite.lb.client/interface/Notification.h b/org.glite.lb.client/interface/Notification.h
new file mode 100644 (file)
index 0000000..d7a32e7
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_NOTIFICATION_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_NOTIFICATION_HPP__
+
+#include "edg/workload/logging/client/consumer.h"
+#include "edg/workload/logging/client/notification.h"
+
+#include "edg/workload/common/jobid/JobId.h"
+#include "edg/workload/logging/client/JobStatus.h"
+
+
+EWL_BEGIN_NAMESPACE; 
+
+
+/** Manage LB notifications.
+ * Simplified API, covers only a subset of C API functinality
+ */
+
+class Notification {
+public:
+       Notification();
+
+       /** Create from NotifId */
+       Notification(const std::string);
+
+       /** Create from server,port pair */
+       Notification(const std::string,const u_int16_t);
+
+       ~Notification();
+
+       std::string getNotifId() const; /**< retrieve NotifId */
+       time_t getValid() const;        /**< until when it is valid */
+       int getFd() const;              /**< local listener filedescriptor */
+
+       /** Add this job to the list.
+        * Local operation only, Register() has to be called
+        * to propagate changes to server 
+        */
+       void addJob(const edg::workload::common::jobid::JobId &); 
+
+       /** Remove job from the list, local op again. */
+       void removeJob(const edg::workload::common::jobid::JobId &);
+
+       /** Get jobs on the list */
+       std::string getJobs();
+
+       /** Receive notifications on these states */
+       void setStates(const std::vector<edg::workload::logging::client::JobStatus::Code> &);
+
+       /** Get states */
+       std::string getStates();
+
+       /** Register (or re-register, i.e. change and extend) 
+        * with the server
+        */
+       void Register();
+
+       /** Receive notification.
+        * Blocks at most the specified timeout (maybe 0 for local polling).
+        * \retval 0 OK
+        * \retval 1 timeout 
+        */
+       int receive(edg::workload::logging::client::JobStatus &,timeval &);
+
+private:
+       std::vector<edg::workload::common::jobid::JobId>        jobs;
+       std::vector<edg::workload::logging::client::JobStatus::Code>    states;
+
+       edg_wll_Context ctx;
+       edg_wll_NotifId notifId;
+       time_t          valid;
+};
+
+
+EWL_END_NAMESPACE;
+
+#endif
diff --git a/org.glite.lb.client/interface/ServerConnection.h b/org.glite.lb.client/interface/ServerConnection.h
new file mode 100644 (file)
index 0000000..fbeb55c
--- /dev/null
@@ -0,0 +1,306 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_SERVERCONNECTION_HPP__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_SERVERCONNECTION_HPP__
+
+#ident "$Header$"
+
+/**
+ * @file ServerConnection.h
+ * @version $Revision$
+ */
+
+#include <string.h>
+#include <list>
+
+#include "edg/workload/common/jobid/JobId.h"
+#include "edg/workload/logging/client/Event.h"
+#include "edg/workload/logging/client/JobStatus.h"
+
+#include "edg/workload/logging/client/consumer.h"
+
+EWL_BEGIN_NAMESPACE;
+
+/** Auxiliary class to hold an atomic query condition. */
+class QueryRecord {
+public:
+       friend class ServerConnection;
+       friend edg_wll_QueryRec *convertQueryVector(const std::vector<QueryRecord> &in);
+
+       /* IMPORTANT: must match lbapi.h */
+       enum Attr {
+               UNDEF=0,        /**< Not-defined value, used to terminate lists etc. */
+               JOBID,          /**< Job Id \see _edg_wll_QueryRec */
+               OWNER,          /**< Job owner \see _edg_wll_QueryRec */
+               STATUS,         /**< Current job status */
+               LOCATION,       /**< Where is the job processed */
+               DESTINATION,    /**< Destination CE */
+               DONECODE,       /**< Minor done status (OK,fail,cancel) */
+               USERTAG,        /**< User tag (not implemented yet) */
+               TIME,           /**< Timestamp \see _edg_wll_QueryRec */
+               LEVEL,          /**< Logging level (see "dglog.h") * \see _edg_wll_QueryRec */
+               HOST,           /**< Where the event was generated */
+               SOURCE,         /**< Source component */
+               INSTANCE,       /**< Instance of the source component */
+               EVENT_TYPE,     /**< Event type \see _edg_wll_QueryRec */
+               CHKPT_TAG,      /**< Checkpoint tag */
+               RESUBMITTED,    /**< Job was resubmitted */
+               PARENT,         /**< Job was resubmitted */
+               EXITCODE,       /**< Unix exit code */
+       };
+
+       enum Op {
+               EQUAL=EDG_WLL_QUERY_OP_EQUAL,
+               LESS=EDG_WLL_QUERY_OP_LESS,
+               GREATER=EDG_WLL_QUERY_OP_GREATER,
+               WITHIN=EDG_WLL_QUERY_OP_WITHIN,
+               UNEQUAL=EDG_WLL_QUERY_OP_UNEQUAL
+       };
+  
+       QueryRecord();
+
+       /* copy and assignment */
+       QueryRecord(const QueryRecord &);
+       QueryRecord& operator=(const QueryRecord &);
+
+       /* constructors for simple attribute queries */
+       QueryRecord(const Attr, const Op, const std::string &);
+       QueryRecord(const Attr, const Op, const int);
+       QueryRecord(const Attr, const Op, const struct timeval &);
+       QueryRecord(const Attr, const Op, const edg::workload::common::jobid::JobId&);
+       /* this one is for attr==TIME and particular state */
+       QueryRecord(const Attr, const Op, const int, const struct timeval &);
+       
+       /* constructors for WITHIN operator */
+       QueryRecord(const Attr, const Op, const std::string &, const std::string &);
+       QueryRecord(const Attr, const Op, const int, const int);
+       QueryRecord(const Attr, const Op, const struct timeval &, const struct timeval &);
+       QueryRecord(const Attr, const Op, const int, const struct timeval &, const struct timeval &);
+
+       /* convenience for user tags */
+       QueryRecord(const std::string &, const Op, const std::string &);
+       QueryRecord(const std::string &, const Op, const std::string &, const std::string &);
+       
+       ~QueryRecord();
+  
+       static const std::string AttrName(const Attr) ;
+  
+protected:
+
+       /* conversion to C API type */
+       operator edg_wll_QueryRec() const;
+
+private:
+       Attr    attr;
+       Op      oper;
+       std::string tag_name;
+       int state;
+       std::string string_value;
+       edg::workload::common::jobid::JobId jobid_value;
+        int     int_value;
+        struct timeval timeval_value;
+       std::string string_value2;
+        int     int_value2;
+        struct timeval timeval_value2;
+};
+
+
+/** Supported aggregate operations */
+enum AggOp { AGG_MIN=1, AGG_MAX, AGG_COUNT };
+
+
+/**
+ * Connection to the L&B server.
+ * Maintain connection to the server.
+ * Implement non job-specific API calls
+ */
+
+class ServerConnection {
+public:
+       friend class Job;
+
+       ServerConnection(void);
+
+       /* DEPRECATED: do not use
+        * connections are now handled automagically inside the implementation
+        */
+       ServerConnection(const std::string &);
+
+       /** Open connection to a given server */
+       void open(const std::string &);
+
+       /** Close the current connection */
+       void close(void);
+
+       /* END DEPRECATED */
+
+       /* set & get parameter methods */
+
+       /* consumer parameter settings */
+       void setQueryServer(const std::string&, int);
+       void setQueryTimeout(int);
+
+       void setX509Proxy(const std::string&);
+       void setX509Cert(const std::string&, const std::string&);
+
+       std::pair<std::string, int> getQueryServer() const;
+       int getQueryTimeout() const;
+
+       std::string getX509Proxy() const;
+       std::pair<std::string, std::string> getX509Cert() const;
+
+       /* end of set & get */
+
+       virtual ~ServerConnection();
+
+
+       /* consumer API */
+
+       /** Retrieve the set of single indexed attributes.
+        * outer vector elements correspond to indices
+        * inner vector elements correspond to index columns
+        * if .first of the pair is USERTAG, .second is its name
+        * if .first is TIME, .second is state name
+        * otherwise .second is meaningless (empty string anyway)
+        */
+       std::vector<std::vector<std::pair<QueryRecord::Attr,std::string> > >
+               getIndexedAttrs(void);
+
+       /** Retrieve hard and soft result set size limit */
+       std::pair<int,int> getLimits(void) const;
+
+       /** Set the soft result set size limit */
+       void setQueryJobsLimit(int);
+       void setQueryEventsLimit(int);
+  
+       /** Retrieve all events satisfying the query records
+        * @param job_cond, event_cond - vectors of conditions to be satisfied 
+        *  by jobs as a whole or particular events, conditions are ANDed
+        * @param events vector of returned events
+        */
+       void queryEvents(const std::vector<QueryRecord>& job_cond,
+                        const std::vector<QueryRecord>& event_cond,
+                        std::vector<Event>&) const;
+  
+       const std::vector<Event> queryEvents(const std::vector<QueryRecord>& job_cond,
+                                            const std::vector<QueryRecord>& event_cond) const;
+
+       const std::list<Event> queryEventsList(const std::vector<QueryRecord>& job_cond,
+                                              const std::vector<QueryRecord>& event_cond) const;
+
+
+       /** The same as queryEvents but return only an aggregate.
+        * @param job_cond, event_cond - vectors of conditions to be satisfied 
+        *  by jobs as a whole or particular events, conditions are ANDed
+        * @param op aggregate operator to apply
+        * @param attr attribute to apply the operation to
+        */
+       std::string queryEventsAggregate(const std::vector<QueryRecord>& job_cond,
+                                        const std::vector<QueryRecord>& event_cond,
+                                        enum AggOp const op,
+                                        std::string const attr) const;
+  
+
+       /** Retrieve all events satisfying the query records
+        * @param job_cond, event_cond - vectors of vectors of job or event conditions, 
+        * respectively. The inner vectors are logically ANDed, the outer are ORed
+        * (cond1 AND cond2 AND ...) OR (condN AND ...)
+        * @param events vector of returned events
+        */
+       void queryEvents(const std::vector<std::vector<QueryRecord> >& job_cond,
+                        const std::vector<std::vector<QueryRecord> >& event_cond,
+                        std::vector<Event>&) const;
+  
+       const std::vector<Event> 
+       queryEvents(const std::vector<std::vector<QueryRecord> >& job_cond,
+                   const std::vector<std::vector<QueryRecord> >& event_cond) const;
+
+       
+       /** Retrieve jobs satisfying the query records, including their states
+        * @param query vector of Query records that are anded to form the
+        *      query
+        * @param ids vector of returned job id's
+        * @param states vector of returned job states
+        */
+  
+       void queryJobs(const std::vector<QueryRecord>& query,
+                      std::vector<edg::workload::common::jobid::JobId>& ids) const;
+  
+       const std::vector<edg::workload::common::jobid::JobId>
+       queryJobs(const std::vector<QueryRecord>& query) const;
+  
+       
+       /** Retrieve jobs satisfying the query records, including their states
+        * @param query vector of Query record vectors that are ORed and ANDed to form the
+        *      query
+        * @param ids vector of returned job id's
+        * @param states vector of returned job states
+        */
+   
+       void queryJobs(const std::vector<std::vector<QueryRecord> >& query,
+                      std::vector<edg::workload::common::jobid::JobId>& ids) const;
+  
+       const std::vector<edg::workload::common::jobid::JobId>
+       queryJobs(const std::vector<std::vector<QueryRecord> >& query) const;
+
+       /** Retrieve jobs satisfying the query records, including status
+        * information
+        * @param query vector of Query records that are anded to form the
+        *      query
+        * @param ids vector of returned job id's
+        * @param states vector of returned job states
+        */
+       void queryJobStates(const std::vector<QueryRecord>& query, 
+                           int flags,
+                           std::vector<JobStatus> & states) const;
+       const std::vector<JobStatus>  queryJobStates(const std::vector<QueryRecord>& query, 
+                                                    int flags) const;
+
+       const std::list<JobStatus>  queryJobStatesList(const std::vector<QueryRecord>& query,
+                                                    int flags) const;
+  
+       /** Retrieve jobs satisfying the query records, including status
+        * information
+        * @param query vector of Query records that are anded to form the
+        *      query
+        * @param ids vector of returned job id's
+        * @param states vector of returned job states
+        */
+       void queryJobStates(const std::vector<std::vector<QueryRecord> >& query, 
+                           int flags,
+                           std::vector<JobStatus> & states) const;
+       const std::vector<JobStatus>  
+       queryJobStates(const std::vector<std::vector<QueryRecord> >& query, 
+                      int flags) const;
+  
+       /** States of all user's jobs.
+        * Convenience wrapper around queryJobs.
+        */
+       void userJobStates(std::vector<JobStatus>& stateList) const;
+       const std::vector<JobStatus> userJobStates() const;
+  
+  
+       /** JobId's of all user's jobs.
+        * Convenience wrapper around queryJobs.
+        */
+       void userJobs(std::vector<edg::workload::common::jobid::JobId> &) const;
+       const std::vector<edg::workload::common::jobid::JobId> userJobs() const;
+
+       /** Manipulate LB parameters, the same as for edg_wll_Context in C */
+       void setParam(edg_wll_ContextParam, int); 
+       void setParam(edg_wll_ContextParam, const std::string); 
+       void setParam(edg_wll_ContextParam, const struct timeval &); 
+
+       int getParamInt(edg_wll_ContextParam) const;
+       std::string getParamString(edg_wll_ContextParam) const;
+       struct timeval getParamTime(edg_wll_ContextParam) const;
+  
+protected:
+
+       edg_wll_Context getContext(void) const;
+
+private:
+       edg_wll_Context context;
+};
+
+EWL_END_NAMESPACE;
+
+#endif
diff --git a/org.glite.lb.client/project/properties.xml b/org.glite.lb.client/project/properties.xml
new file mode 100755 (executable)
index 0000000..0d9e1e1
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB client component common properties">
+
+       <property file="build.properties" />    
+       <property name="subsystem.name" value="${lb.subsystem.name}"/>
+       <property name="subsystem.prefix" value="${lb.subsystem.prefix}"/>
+       <property name="component.prefix" value="client" />
+
+       <import file="${component.general.properties.file}" />
+
+</project>
diff --git a/org.glite.lb.client/project/taskdefs.xml b/org.glite.lb.client/project/taskdefs.xml
new file mode 100755 (executable)
index 0000000..9125c37
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB Subsystem common tasks and types definitions">
+</project>
diff --git a/org.glite.lb.client/src/Event.cpp.T b/org.glite.lb.client/src/Event.cpp.T
new file mode 100644 (file)
index 0000000..cf24f00
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+@@@AUTO
+*/
+
+#include <utility>
+#include <vector>
+#include <string>
+#include <stdio.h>
+
+#include <errno.h>
+
+#include "glite/wms/jobid/cjobid.h"
+
+#include "Event.h"
+#include "glite/wms/jobid/JobIdExceptions.h"
+#include "LoggingExceptions.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/events.h"
+#include "glite/lb/notifid.h"
+
+EWL_BEGIN_NAMESPACE;
+
+#define CLASS_PREFIX "edg::workload::logging::Event::"
+
+@@@{
+sub typeswitch {
+       my $ftype = shift;
+       my $resc = shift;
+       local $_;
+       my %ctype;
+       $ctype{$_} = 1 while $_ = shift;
+
+       $resc = 'break;' unless $resc;
+
+       gen $indent."switch (attr) {\n";
+       selectType $event $ftype;
+       for (getFields $event) {
+               my $f = selectField $event $_;
+               if($ctype{$f->{type}}) {
+                 my $cstr = $ftype eq '_common_' ? 'any' : lcfirst $ftype;
+                 my $cname = getName $f 'C';
+                 gen "$indent\tcase Event::".uc($f->{name}).": return(cev->$cstr.$cname);\n";
+               } elsif (($f->{type} eq "int") && 
+                        ($ctype{"string"}) && 
+                        $f->{codes}) {
+                 # conversion from int to string (well, enum to string)
+                 my $cstr = $ftype eq '_common_' ? 'any' : lcfirst $ftype;
+                 my $cname = getName $f 'C';
+                 my $fn = $ftype eq '_common_' ? "" : ucfirst $ftype;
+                 my $c =  $fn . ucfirst $f->{name}; 
+                 $cast = ($c eq 'Level') ? "(edg_wll_Level)" : "";
+                 gen "$indent\tcase Event::".uc($f->{name}).": return((const char *)edg_wll_${c}ToString(${cast}cev->$cstr.$cname));\n";
+               } elsif (($f->{type} eq "jobid") &&
+                        ($ctype{"string"})) {
+                  # conversion from jobid to string
+               }
+       }
+       gen "$indent\tdefault: $resc\n" if $resc;
+       gen "$indent\}\n";
+}
+@@@}
+
+Event::Event(void)
+{
+  type = UNDEF;
+  flesh = 0;
+}
+
+
+Event::Event(const Event &in)
+{
+  type = in.type;
+  flesh = in.flesh;
+  if (flesh) flesh->use();
+}
+
+
+Event::Event(edg_wll_Event *in)
+{
+  type = (Type)in->type;
+  flesh = new CountRef<Event>((void*)in);
+}
+
+
+Event::~Event(void)
+{
+  if (flesh) flesh->release();
+}
+
+
+Event & 
+Event::operator= (const Event &in)
+{
+  if (flesh) flesh->release();
+  type = in.type;
+  flesh = in.flesh;
+  if (flesh) flesh->use();
+  
+  return *this;
+}
+
+int Event::getValInt(Attr attr)        const
+{
+       edg_wll_Event const     *cev = (edg_wll_Event *) flesh->ptr;
+
+@@@{
+       $indent = "\t";
+       typeswitch '_common_',undef,'int','port','logsrc';
+@@@}
+
+       switch (cev->type) {
+@@@{
+       $indent = "\t\t";
+       for my $t (getTypes $event) {
+               gen "\t\tcase ".uc($t).":\n";
+               typeswitch $t,'goto badattr;','int','port','logsrc';
+       }
+@@@}
+               default:
+                 STACK_ADD;
+                 throw(Exception(EXCEPTION_MANDATORY, EINVAL,
+                                 "attribute is not of int type"));
+        }
+badattr:
+        STACK_ADD;
+        throw(Exception(EXCEPTION_MANDATORY, ENOENT, "invalid attribute"));
+       return -1; /* gcc, shut up! */
+}
+
+static char const *get_string_val(const edg_wll_Event *cev, Event::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_',undef,'string';
+@@@}
+
+       switch (cev->type) {
+@@@{
+       $indent = "\t\t";
+       for my $t (getTypes $event) {
+               gen "\t\tcase Event::".uc($t).":\n";
+               typeswitch $t,'goto badattr;','string';
+       }
+@@@}
+               default:
+                 STACK_ADD;
+                 throw(Exception(EXCEPTION_MANDATORY, EINVAL, 
+                                 "attribute is not of string type and can not be converted"));
+       }
+badattr:
+       STACK_ADD;
+       throw(Exception(EXCEPTION_MANDATORY, ENOENT, "invalid attribute"));
+       return NULL; /* gcc, shut up! */
+}
+
+
+std::string 
+Event::getValString(Attr attr) const
+{
+  edg_wll_Event const *cev = (edg_wll_Event *) flesh->ptr;
+  std::string  ret;
+  
+  try {
+    char const *s = get_string_val(cev,attr);
+    if (s) ret.assign(s);
+    return ret;
+  } catch (Exception &e) {
+    STACK_ADD;
+    throw;
+  }
+}
+
+
+struct timeval 
+Event::getValTime(Attr attr) const
+{
+  edg_wll_Event const *cev = (edg_wll_Event *) flesh->ptr;
+@@@{
+    $indent = "\t";
+    typeswitch '_common_',undef,'timeval';
+@@@}
+  
+  /* XXX
+   * to make things simpler we don't include this here as there are no
+   * type specific timeval attributes currently
+   *
+   *   switch (cev->type) {
+   */
+
+  STACK_ADD; 
+  throw(Exception(EXCEPTION_MANDATORY, ENOENT, "invalid attribute"));
+}
+
+static
+edg_wlc_JobId 
+get_val_jobid(edg_wll_Event const *cev, Event::Attr attr) 
+{
+@@@{
+        $indent = "\t";
+        typeswitch '_common_', undef,'jobid';
+@@@}
+       switch (cev->type) {
+@@@{
+       $indent = "\t\t";
+       for my $t (getTypes $event) {
+               gen "\t\tcase Event::".uc($t).":\n";
+               typeswitch $t,'goto badattr;','jobid';
+       }
+@@@}
+               default:
+                 STACK_ADD;
+                 throw(Exception(EXCEPTION_MANDATORY, EINVAL, 
+                                 "attribute is not of jobid type"));
+       }
+badattr:
+       STACK_ADD;
+       throw(Exception(EXCEPTION_MANDATORY, ENOENT, "invalid attribute"));
+       return NULL; /* gcc, shut up! */
+}
+
+const 
+edg::workload::common::jobid::JobId 
+Event::getValJobId(Attr attr) const
+{
+       edg_wll_Event const *cev = (edg_wll_Event *) flesh->ptr;
+       try {
+               edg_wlc_JobId job_id = get_val_jobid(cev,attr);
+               return(edg::workload::common::jobid::JobId(job_id));
+       } 
+       catch (Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+
+}
+
+
+static std::string const names[Event::TYPE_MAX] = {
+  "undefined",
+@@@{
+  for (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+        $event->getTypes)
+  {
+    gen "\t\"$_\",\n";
+  }
+@@@}
+};
+
+const std::string  & 
+Event::name(void) const
+{
+  if (type<0 || type>TYPE_MAX) {
+    STACK_ADD;
+    throw(Exception(EXCEPTION_MANDATORY, EINVAL, "invalid event type"));
+  }
+  return names[type];
+}
+
+
+static 
+std::string const attr_names[Event::ATTR_MAX] = {
+@@@{
+       for (sort {$a cmp $b} getAllFields $event) {
+               gen "\t\"$_\",\n";
+       }
+@@@}
+};
+
+
+const std::string & 
+Event::getAttrName(Attr attr) const
+{
+  if (attr<0 || attr>=ATTR_MAX) {
+    STACK_ADD;
+    throw(Exception(EXCEPTION_MANDATORY, EINVAL, "invalid attribute"));
+  }
+  return attr_names[attr];
+}
+
+
+typedef std::pair<Event::Attr,Event::AttrType> tpair;
+static std::vector<tpair> attrs[Event::TYPE_MAX];
+
+#define apush(etype,attr,atype) \
+       attrs[etype].push_back(tpair(attr,atype))
+
+static bool attrs_inited = false;
+
+static void init_attrs(void)
+{
+@@@{
+       for my $t (getTypes $event) {
+               my $tu = uc $t;
+               selectType $event '_common_';
+               for (getFields $event) {
+                       my $fu = uc $_;
+                       my $f = selectField $event $_;
+                       my $ftu = uc "$f->{type}_T";
+                       gen "\tapush(Event::$tu,Event::$fu,Event::$ftu);\n";
+               }
+               selectType $event $t;
+               for (getFields $event) {
+                       my $fu = uc $_;
+                       my $f = selectField $event $_;
+                       my $ftu = uc "$f->{type}_T";
+                       gen "\tapush(Event::$tu,Event::$fu,Event::$ftu);\n";
+               }
+       }
+@@@}
+}
+
+std::vector<std::pair<Event::Attr,Event::AttrType> > const & Event::getAttrs(void) const
+{
+  if (type<0 || type>=TYPE_MAX) {
+    STACK_ADD;
+    throw(Exception(EXCEPTION_MANDATORY, EINVAL, "invalid event type"));
+  }
+  
+  /* FIXME: thread safety */
+  if (!attrs_inited) {
+    init_attrs();
+    attrs_inited = true;
+  }
+  
+  return attrs[type];
+}
+
+
+void 
+Event::destroyFlesh(void *in) 
+{
+  edg_wll_FreeEvent((edg_wll_Event *) in);
+  free(in);
+}
+
+EWL_END_NAMESPACE;
diff --git a/org.glite.lb.client/src/Job.cpp b/org.glite.lb.client/src/Job.cpp
new file mode 100644 (file)
index 0000000..a37aa50
--- /dev/null
@@ -0,0 +1,209 @@
+#ident "$Header$"
+
+/**
+ * @file Job.cpp
+ * @version $Revision$
+ */
+
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+
+#include "Job.h"
+#include "glite/wms/jobid/JobIdExceptions.h"
+#include "LoggingExceptions.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+
+EWL_BEGIN_NAMESPACE;
+
+#define CLASS_PREFIX "edg::workload::logging::Job::"
+
+const int Job::STAT_CLASSADS = EDG_WLL_STAT_CLASSADS;
+const int Job::STAT_CHILDREN = EDG_WLL_STAT_CHILDREN;
+const int Job::STAT_CHILDSTAT = EDG_WLL_STAT_CHILDSTAT;
+
+Job::Job(void) 
+{
+}
+
+
+Job::Job(const edg::workload::common::jobid::JobId &in) 
+{
+  try {
+    jobId = in;
+  } catch (Exception &e) {
+    STACK_ADD;
+    throw;
+  }
+}
+
+
+Job::~Job(void) 
+{
+}
+
+
+Job & Job::operator= (const edg::workload::common::jobid::JobId &in) 
+{
+  try {
+    jobId = in;
+    return *this;
+  } catch (Exception &) {
+    STACK_ADD;
+    throw;
+  }
+}
+
+
+JobStatus 
+Job::status(int flags) const
+{
+  JobStatus      jobStatus;
+
+  try {
+    edg_wll_JobStat    *cstat = jobStatus.c_ptr();
+    int ret = edg_wll_JobStatus(server.getContext(),
+                               jobId, // automagically converted by member operator
+                               flags,
+                               cstat);
+    check_result(ret,
+                server.getContext(),
+                "edg_wll_JobStatus");
+
+/* XXX the enums match due to automatic generation */
+    jobStatus.status = (JobStatus::Code) cstat->state;
+    
+    return(jobStatus);
+
+  } catch (Exception &e) {
+    STACK_ADD;
+    throw;
+  }
+}
+
+
+void 
+Job::log(std::vector<Event> &eventList) const
+{
+  edg_wll_Event *events = NULL,*ev;
+  int             result, qresults_param;
+  char            *errstr = NULL;
+  edg_wll_Context context;
+
+  try {
+    context = server.getContext();
+    result = edg_wll_JobLog(context, jobId, &events);
+    if (result == E2BIG) {
+           edg_wll_Error(context, NULL, &errstr);
+           check_result(edg_wll_GetParam(context,
+                                   EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                           context,
+                           "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+           if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                   edg_wll_SetError(context, result, errstr);
+                   check_result(result, context,"edg_wll_JobLog");
+               }
+    } else {
+           check_result(result, context,"edg_wll_JobLog");
+    }
+
+    for (int i=0; events[i].type != EDG_WLL_EVENT_UNDEF; i++) {
+       ev = (edg_wll_Event *) malloc(sizeof *ev);
+       memcpy(ev,events+i,sizeof *ev);
+      eventList.push_back(Event(ev)); 
+    }
+
+    free(events);
+
+    if (result) {
+           edg_wll_SetError(context, result, errstr);
+           check_result(result, context,"edg_wll_JobLog");
+    }
+  } catch (Exception &e) {
+    if(errstr) free(errstr);
+
+    STACK_ADD;
+    throw;
+  }
+
+}
+
+
+const std::vector<Event> 
+Job::log(void) const
+{
+  std::vector<Event>   eventList;
+  
+  log(eventList);
+  return(eventList);
+}
+
+
+const std::pair<std::string,u_int16_t> 
+Job::queryListener(std::string const & name) const
+{
+  std::string  host;
+  char *c_host = NULL;
+  uint16_t   port;
+
+  try {
+    int ret = edg_wll_QueryListener(server.getContext(),
+                                      jobId,
+                                      name.c_str(),
+                                      &c_host,
+                                      &port);
+    check_result(ret,
+                server.getContext(),
+                "edg_wll_QueryListener");
+    
+    host = c_host;
+    free(c_host);
+    return(std::pair<std::string,u_int16_t>(host,port));    
+
+  } catch (Exception &e) {
+    if(c_host) free(c_host);
+    STACK_ADD;
+    throw;
+  }
+}
+
+
+void Job::setParam(edg_wll_ContextParam par, int val)
+{
+       server.setParam(par,val);
+}
+
+void Job::setParam(edg_wll_ContextParam par, const std::string val)
+{
+       server.setParam(par,val);
+}
+void Job::setParam(edg_wll_ContextParam par, const struct timeval & val)
+{
+       server.setParam(par,val);
+}
+
+int Job::getParamInt(edg_wll_ContextParam par) const
+{
+       return server.getParamInt(par);
+}
+
+std::string Job::getParamString(edg_wll_ContextParam par) const
+{
+       return server.getParamString(par);
+}
+
+struct timeval Job::getParamTime(edg_wll_ContextParam par) const
+{
+       return server.getParamTime(par);
+}
+
+  
+
+EWL_END_NAMESPACE;
diff --git a/org.glite.lb.client/src/JobStatus.cpp.T b/org.glite.lb.client/src/JobStatus.cpp.T
new file mode 100644 (file)
index 0000000..6e09ac7
--- /dev/null
@@ -0,0 +1,481 @@
+#include <utility>
+#include <vector>
+#include <string>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "JobStatus.h"
+#include "LoggingExceptions.h"
+#include "glite/wms/jobid/JobId.h"
+
+#include "glite/lb/consumer.h"
+
+
+EWL_BEGIN_NAMESPACE;
+
+#define CLASS_PREFIX "edg::workload::logging::JobStatus::"
+
+@@@{
+sub typeswitch {
+        my ($ftype,$ctype,$resc) = @_;
+        local $_;
+
+       $resc = "break;" unless $resc;
+
+        gen $indent."switch (attr) {\n";
+        selectType $status $ftype;
+        for (getFields $status) {
+                my $f = selectField $status $_;
+                if($f->{type} eq $ctype) {
+                 my $fnu = $f->{name};
+                 $fnu =~ s/([a-z])([A-Z])/$1_$2/g;
+                 $fnu = uc $fnu;
+                 my $cname = getName $f 'C';
+                 gen "$indent\tcase JobStatus::$fnu: return cstat->$cname;\n"
+               };
+               # XXX: when there are conversion functions, we may get these as strings as well
+               #elsif (($f->{type} eq "int") && 
+               #        ($ctype eq "string") && 
+               #        $f->{codes}) {
+               #  my $fnu = $f->{name};
+               #  $fnu =~ s/([a-z])([A-Z])/$1_$2/g;
+               #  $fnu = uc $fnu;
+               #  my $cname = getName $f 'C';
+               #  my $fn = $ftype eq '_common_' ? "" : ucfirst $ftype;
+               #  my $c =  $fn . ucfirst $f->{name}; 
+               #  $cast = ($c eq 'Level') ? "(edg_wll_Level)" : "";
+               #  gen "$indent\tcase JobStatus::$fnu: return((const char *)edg_wll_${c}ToString(${cast}cstat->$cname));\n";            }
+        }
+        gen "$indent\tdefault: $resc\n" if $resc;
+        gen "$indent\}\n";
+}
+@@@}
+
+JobStatus::JobStatus(void)
+{
+  status = UNDEF;
+  flesh = 0;
+}
+
+JobStatus::JobStatus(const JobStatus & in)
+{
+  status = in.status;
+  flesh = in.flesh;
+  if (flesh) flesh->use();
+}
+
+JobStatus & 
+JobStatus::operator=(const JobStatus & in)
+{
+  if (flesh) flesh->release();
+  status = in.status;
+  flesh = in.flesh;
+  if (flesh) flesh->use();
+  return *this;
+}
+
+JobStatus::JobStatus(const edg_wll_JobStat & in)
+{
+  status = (Code)in.state;
+  flesh = new CountRef<JobStatus>((void*)&in);
+}
+JobStatus & 
+JobStatus::operator=(const edg_wll_JobStat & in)
+{
+  if(flesh)
+    flesh->release();
+  status = (Code)in.state;
+  flesh = new CountRef<JobStatus>((void*)&in);
+  return(*this);
+}
+
+JobStatus::~JobStatus()
+{
+  if (flesh) flesh->release();
+}
+
+edg_wll_JobStat *
+JobStatus::c_ptr(void)
+{
+  edg_wll_JobStat *s;
+
+  if(flesh)
+    return((edg_wll_JobStat*)flesh->ptr);
+
+  s = new edg_wll_JobStat;
+  // XXX - is it neccessary? new should throw exception itself... 
+  if(!s) throw(Exception(EXCEPTION_MANDATORY,
+                        ENOMEM,
+                        "out of memory allocating c-struct for JobStatus"));
+
+  edg_wll_InitStatus(s);
+  flesh = new CountRef<JobStatus>((void*)s);
+  return(s);
+}
+
+
+static std::string const names[JobStatus::CODE_MAX] = {
+       "undefined",
+@@@{
+       for (sort { $status->{order}->{$a} <=> $status->{order}->{$b} }
+               $status->getTypes)
+       {
+               gen "\t\"$_\",\n";
+       }
+@@@}
+};
+
+const std::string& JobStatus::name(void) const
+{
+  if (status<0 || status>=CODE_MAX) {
+    STACK_ADD;
+    throw(Exception(EXCEPTION_MANDATORY, 
+                   EINVAL,
+                   "status code invalid"));
+  }
+  return names[status];
+}
+
+int JobStatus::getValInt(JobStatus::Attr attr) const
+{
+  edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+@@@{
+    $indent = "\t";
+    typeswitch '_common_','int';
+@@@}
+
+ STACK_ADD; 
+ throw(Exception(EXCEPTION_MANDATORY,
+                ENOENT,
+                "no such attribute"));
+ return -1;    /* make gcc shut up -- never returns */
+}
+
+bool JobStatus::getValBool(JobStatus::Attr attr) const
+{
+  edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+@@@{
+    $indent = "\t";
+    typeswitch '_common_','bool';
+@@@}
+
+ STACK_ADD; 
+ throw(Exception(EXCEPTION_MANDATORY,
+                ENOENT,
+                "no such attribute"));
+ return -1;    /* make gcc shut up -- never returns */
+}
+
+static const char *
+get_string_val(const edg_wll_JobStat *cstat,JobStatus::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','string';
+@@@}
+ STACK_ADD;
+ throw(Exception(EXCEPTION_MANDATORY,
+                ENOENT,
+                "no such attribute"));
+ return 0;     /* make gcc shut up -- never returns */
+}
+
+std::string 
+JobStatus::getValString(JobStatus::Attr attr) const
+{
+  edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+  std::string  ret;
+  
+  try{
+    const char *s = get_string_val(cstat,attr);
+    if (s) ret.assign(s);
+    return ret;
+  } catch (Exception &e) {
+    STACK_ADD;
+    throw;
+  }
+}
+
+
+struct timeval 
+JobStatus::getValTime(Attr attr) const
+{
+  edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','timeval';
+@@@}
+ STACK_ADD;
+ throw(Exception(EXCEPTION_MANDATORY,
+                ENOENT,
+                "no such attribute"));
+}
+
+
+const
+edg_wlc_JobId 
+get_val_jobid(edg_wll_JobStat const *cstat, JobStatus::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','jobid';
+@@@}
+ STACK_ADD;
+ throw(Exception(EXCEPTION_MANDATORY,
+                ENOENT,
+                "no such attribute"));
+ return 0;     /* make gcc shut up -- never returns */
+}
+
+
+const 
+edg::workload::common::jobid::JobId  
+JobStatus::getValJobId(Attr attr) const
+{
+  edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+
+  try {
+         edg_wlc_JobId job_id = get_val_jobid(cstat, attr);
+         return(edg::workload::common::jobid::JobId(job_id));
+  }
+  catch(Exception &e) {
+         STACK_ADD;
+         throw;
+  }
+}
+
+const
+int *
+get_val_intlist(edg_wll_JobStat const *cstat, JobStatus::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','intlist';
+@@@}
+  STACK_ADD;
+  throw(Exception(EXCEPTION_MANDATORY,
+                 ENOENT,
+                 "no such attribute"));
+  return 0;    /* make gcc shut up -- never returns */
+}
+
+
+const 
+std::vector<int>
+JobStatus::getValIntList(Attr attr) const
+{
+       edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+
+       std::vector<int> result;
+       const int *r;
+       
+       try {
+               r = get_val_intlist(cstat, attr);
+               if(r)
+                       for(int i = 1; i <= r[0]; i++)
+                               result.push_back(r[i]);
+       } catch(Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+       return result;
+}
+
+
+char ** const
+get_val_stringlist(edg_wll_JobStat const *cstat, JobStatus::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','strlist';
+@@@}
+  STACK_ADD;
+  throw(Exception(EXCEPTION_MANDATORY,
+                 ENOENT,
+                 "no such attribute"));
+  return 0;    /* make gcc shut up -- never returns */
+}
+
+
+const
+std::vector<std::string>
+JobStatus::getValStringList(Attr attr) const
+{
+       edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+
+       std::vector<std::string> result;
+       char **r , **p;
+
+       try {
+               r = (char**)get_val_stringlist(cstat, attr);
+               if(r)
+                       for(p = r; *p; p++)
+                               result.push_back(std::string(*p));
+       } catch(Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+       return result;
+}
+
+
+edg_wll_TagValue * const
+get_val_taglist(edg_wll_JobStat const *cstat, JobStatus::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','taglist';
+@@@}
+  STACK_ADD;
+  throw(Exception(EXCEPTION_MANDATORY,
+                 ENOENT,
+                 "no such attribute"));
+  return 0;    /* make gcc shut up -- never returns */
+}
+
+
+const
+std::vector<std::pair<std::string,std::string> >
+JobStatus::getValTagList(Attr attr) const
+{
+       edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+
+       std::vector<std::pair<std::string,std::string> > result;
+       edg_wll_TagValue *r , *p;
+
+       try {
+               r = get_val_taglist(cstat, attr);
+               if(r)
+                       for(p = r; p->tag ; p++)
+                               result.push_back(std::pair<std::string,std::string>
+                                                       (std::string(p->tag),std::string(p->value)));
+       } catch(Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+       return result;
+}
+
+
+const
+edg_wll_JobStat *
+get_val_stslist(edg_wll_JobStat const *cstat, JobStatus::Attr attr)
+{
+@@@{
+       $indent = "\t";
+       typeswitch '_common_','stslist';
+@@@}
+  STACK_ADD;
+  throw(Exception(EXCEPTION_MANDATORY,
+                 ENOENT,
+                 "no such attribute"));
+  return 0;    /* make gcc shut up -- never returns */
+}
+
+
+const
+std::vector<JobStatus>
+JobStatus::getValJobStatusList(Attr attr) const
+{
+       edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr;
+
+       std::vector<JobStatus> result;
+       const edg_wll_JobStat *r, *p;
+
+       try {
+               r = get_val_stslist(cstat, attr);
+               if(r)
+                       for(p=r; p->state != EDG_WLL_JOB_UNDEF; p++) {
+                               edg_wll_JobStat *jsep = new edg_wll_JobStat;
+                               if (!edg_wll_CpyStatus(p,jsep)) {
+                                       STACK_ADD;
+                                       throw(Exception(EXCEPTION_MANDATORY,
+                                       ENOMEM,
+                                       "cannot copy edg_wll_JobStat"));
+                               }
+                               result.push_back(JobStatus(*jsep));
+                       }
+       } catch(Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+       return(result);
+}
+
+
+static std::string const attr_names[JobStatus::ATTR_MAX] = {
+@@@{
+       for (sort {$a cmp $b} getAllFields $status) {
+               gen "\t\"$_\",\n";
+       }
+@@@}
+};
+
+const std::string & 
+JobStatus::getAttrName(JobStatus::Attr attr) const
+{
+  if (attr<0 || attr>=ATTR_MAX) {
+    STACK_ADD;
+    throw(Exception(EXCEPTION_MANDATORY,
+                   ENOENT,
+                   "no such attribute"));
+  }
+
+  return attr_names[attr];
+}
+
+
+typedef std::pair<JobStatus::Attr,JobStatus::AttrType> tpair;
+static std::vector<tpair> attrs;
+
+static bool attrs_inited = false;
+
+static void init_attrs(void)
+{
+/* XXX: currently only common attributes in JobStatus */
+@@@{
+       selectType $status '_common_';
+       for (getFields $status) {
+               my $fu = $_;
+               my $f = selectField $status $_;
+                my $ftu = uc "$f->{type}_T";
+               $fu =~ s/([a-z])([A-Z])/$1_$2/g;
+               $fu = uc $fu;
+
+               gen "\tattrs.push_back(tpair(JobStatus::$fu,JobStatus::$ftu));\n";
+       }
+@@@}
+}
+
+
+const std::vector<tpair>& 
+JobStatus::getAttrs(void) const
+{
+  if (status<0 || status>=CODE_MAX) {
+    STACK_ADD;
+    throw(Exception(EXCEPTION_MANDATORY,
+                   EINVAL,
+                   "status code invalid"));
+  }
+
+/* FIXME: thread safety */
+  if (!attrs_inited) {
+    init_attrs();
+    attrs_inited = true;
+  }
+  return attrs;
+}
+
+void 
+JobStatus::destroyFlesh(void *p)
+{
+  edg_wll_JobStat *stat = (edg_wll_JobStat *) p;
+  if (stat) {
+    edg_wll_FreeStatus(stat);
+    free(stat);
+  }
+}
+
+EWL_END_NAMESPACE;
diff --git a/org.glite.lb.client/src/Notification.cpp b/org.glite.lb.client/src/Notification.cpp
new file mode 100644 (file)
index 0000000..6b73c1e
--- /dev/null
@@ -0,0 +1,276 @@
+#ident "$Header$"
+
+/**
+ * @file Notification.cpp
+ * @version $Revision$
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <string>
+#include <vector>
+
+#include "Notification.h"
+#include "JobStatus.h"
+#include "LoggingExceptions.h"
+#include "ServerConnection.h"
+
+#include "glite/lb/notifid.h"
+#include "glite/lb/notification.h"
+
+EWL_BEGIN_NAMESPACE;
+
+#define CLASS_PREFIX "edg::workload::logging::Notification::"
+
+/* external prototypes */
+extern edg_wll_QueryRec **
+convertQueryVectorExt(const std::vector<std::vector<edg::workload::logging::client::QueryRecord> > &);
+
+extern void
+freeQueryRecVector(edg_wll_QueryRec *);
+
+/* Constructors */
+Notification::Notification(void) 
+{
+   try {
+       int ret = edg_wll_InitContext(&ctx);
+       check_result(ret,ctx,"edg_wll_InitContext");
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+Notification::Notification(const std::string notifid_str)
+{
+   try {
+       int ret = edg_wll_InitContext(&ctx);
+       check_result(ret,ctx,"edg_wll_InitContext");
+       ret = edg_wll_NotifIdParse(notifid_str.c_str(),&notifId);
+       check_result(ret,ctx,"edg_wll_NotifIdParse");
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+Notification::Notification(const std::string host,const u_int16_t port)
+{
+   try {
+       int ret = edg_wll_InitContext(&ctx);
+       check_result(ret,ctx,"edg_wll_InitContext");
+       edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER, host.c_str());
+       edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER_PORT, port);
+       ret = edg_wll_NotifIdCreate(host.c_str(),port,&notifId);
+       check_result(ret,ctx,"edg_wll_NotifIdCreate");
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+/* Destructor */
+Notification::~Notification(void) 
+{ 
+   try {
+       edg_wll_FreeContext(ctx);
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+/* Methods */
+std::string
+Notification::getNotifId(void) const
+{
+   try {
+       std::string notifid_str = edg_wll_NotifIdUnparse(notifId);
+       return(notifid_str);
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+time_t
+Notification::getValid(void) const
+{
+   return(valid);
+}
+
+int
+Notification::getFd(void) const
+{
+   try {
+       int ret = edg_wll_NotifGetFd(ctx);
+       check_result(ret,ctx,"edg_wll_NotifGetFd");
+       return(ret);
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+void
+Notification::addJob(const edg::workload::common::jobid::JobId &jobId)
+{
+   std::vector<edg::workload::common::jobid::JobId>::iterator it;
+
+   try {
+       for( it = jobs.begin(); it != jobs.end(); it++ ) {
+               if ( (*it).toString() == jobId.toString() ) {
+                       STACK_ADD;
+                       throw Exception(EXCEPTION_MANDATORY, EINVAL, "job already exists");
+               }
+       }
+       jobs.push_back(jobId);
+
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+void
+Notification::removeJob(const edg::workload::common::jobid::JobId &jobId)
+{
+   std::vector<edg::workload::common::jobid::JobId>::iterator it;
+   int removed = 0;
+
+   try {
+       for( it = jobs.begin(); it != jobs.end(); it++ ) {
+               if ( (*it).toString() == jobId.toString() ) {
+                       jobs.erase(it);
+                       removed += 1;
+//                     break;
+               }
+       }
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+
+   if (removed == 0) {
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, EINVAL, "no job to remove");
+   }
+}
+
+std::string 
+Notification::getJobs(void)
+{
+   std::vector<edg::workload::common::jobid::JobId>::iterator it;
+   std::string ret="";
+
+   try {
+       for( it = jobs.begin(); it != jobs.end(); it++ ) {
+               ret += (*it).toString();
+               ret += "\n";
+       }
+       return ret;
+
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+void
+Notification::setStates(const std::vector<edg::workload::logging::client::JobStatus::Code> &jobStates)
+{
+       states = jobStates;     
+}
+
+std::string 
+Notification::getStates(void)
+{
+   std::vector<edg::workload::logging::client::JobStatus::Code>::iterator it;
+   JobStatus js;
+   std::string ret="";
+
+   try {
+       for( it = states.begin(); it != states.end(); it++ ) {
+               js.status = (*it);
+               ret += js.name();
+               ret += "\n";
+       }
+       return ret;
+
+   } catch (Exception &e) {
+       STACK_ADD;
+       throw;
+   }
+}
+
+void
+Notification::Register(void)
+{
+   int ret = 0;
+   std::vector<edg::workload::common::jobid::JobId>::iterator it;
+   std::vector<edg::workload::logging::client::JobStatus::Code>::iterator its;
+   std::vector<std::vector<edg::workload::logging::client::QueryRecord> > query;
+   edg_wll_QueryRec **conditions = NULL;
+   unsigned i;
+
+   try {
+       /* fill in the query: */
+       for( it = jobs.begin(); it != jobs.end(); it++ ) {
+               std::vector<edg::workload::logging::client::QueryRecord> queryjob;
+
+               QueryRecord r0(QueryRecord::JOBID,QueryRecord::EQUAL,*it);
+               queryjob.push_back(r0);
+               
+               for( its = states.begin(); its != states.end(); its++ ) {
+                       QueryRecord r(QueryRecord::STATUS,QueryRecord::EQUAL,*its);
+                       queryjob.push_back(r);
+               }
+
+               query.push_back(queryjob);
+       }
+       /* convert query to conditions */
+       conditions = convertQueryVectorExt(query);
+       /* register */
+       ret = edg_wll_NotifNew(ctx,conditions,-1,NULL,&notifId,&valid);
+       check_result(ret,ctx,"edg_wll_NotifNew");
+       /* clean */
+       if (conditions) {
+               for( i = 0; conditions[i]; i++ ) {
+// FIXME: not working :o(
+//                     freeQueryRecVector(conditions[i]);
+                       delete[] conditions[i];
+               }
+               delete[] conditions;
+       }
+   } catch (Exception &e) {
+       /* clean */
+       if (conditions) {
+               for( i = 0; conditions[i]; i++ ) {
+// FIXME: not working :o(
+//                     freeQueryRecVector(conditions[i]);
+                       delete[] conditions[i];
+               }
+               delete[] conditions;
+       }
+       STACK_ADD;
+       throw;
+   }
+}
+
+int Notification::receive(edg::workload::logging::client::JobStatus &jobStatus,timeval &timeout)
+{
+       int ret = 0;
+       edg_wll_JobStat *status = (edg_wll_JobStat *) calloc(1,sizeof(edg_wll_JobStat));
+       if (status == NULL) {
+                       STACK_ADD;
+                       throw OSException(EXCEPTION_MANDATORY, ENOMEM, "allocating jobStatus");
+       }
+       ret = edg_wll_NotifReceive(ctx,-1,&timeout,status,&notifId);
+       check_result(ret,ctx,"edg_wll_NotifReceive");
+       jobStatus = JobStatus(*status);
+       return(ret);
+}
+
+EWL_END_NAMESPACE;
diff --git a/org.glite.lb.client/src/ServerConnection.cpp b/org.glite.lb.client/src/ServerConnection.cpp
new file mode 100644 (file)
index 0000000..015cdeb
--- /dev/null
@@ -0,0 +1,1302 @@
+//#ident "$Header$"
+
+/**
+ * @file ServerConnection.cpp
+ * @version $Revision$
+ */
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <time.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include <expat.h>
+
+#include "glite/wms/jobid/JobId.h"
+#include "glite/wms/jobid/JobIdExceptions.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/xml_conversions.h"
+
+#include "ServerConnection.h"
+#include "LoggingExceptions.h"
+
+///using namespace edg::workload::logging;
+
+EWL_BEGIN_NAMESPACE;
+
+/**
+ * definitions of QueryRecord class
+ */
+#define CLASS_PREFIX "edg::workload::logging::QueryRecord::"
+
+
+QueryRecord::QueryRecord(const Attr a, 
+                        const Op o, 
+                        const std::string & v)
+       : attr(a), oper(o),  state(EDG_WLL_JOB_UNDEF), string_value(v)
+{
+       switch(a) {
+       case OWNER:
+       case LOCATION:
+       case DESTINATION:
+       case HOST:
+       case INSTANCE:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "invalid value for attribute: " + v);
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a, 
+                        const Op o, 
+                        const int v)
+  : attr(a), oper(o), state(EDG_WLL_JOB_UNDEF), int_value(v)
+{
+       switch(a) {
+       case DONECODE:
+       case STATUS:
+       case SOURCE:
+       case EVENT_TYPE:
+       case LEVEL:
+       case EXITCODE:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of integer type");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a, 
+                        const Op o, 
+                        const struct timeval& v)
+  : attr(a), oper(o), state(EDG_WLL_JOB_UNDEF), timeval_value(v)
+{
+       switch(a) {
+       case TIME:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of timeval type");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a, 
+                        const Op o, 
+                        const edg::workload::common::jobid::JobId& v)
+       : attr(a), oper(o), state(EDG_WLL_JOB_UNDEF), jobid_value(v)
+{
+       switch(a) {
+       case JOBID:
+       case PARENT:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of JobId type");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a,
+                        const Op o,
+                        const int s,
+                        const struct timeval &v)
+       : attr(a), oper(o), state(s), timeval_value(v)
+{
+       switch(a) {
+       case TIME:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of timeval type");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a,
+                        const Op o,
+                        const std::string &v1,
+                        const std::string &v2)
+       : attr(a), oper(o), state(EDG_WLL_JOB_UNDEF), string_value(v1), string_value2(v2)
+{
+       switch(a) {
+       case OWNER:
+       case LOCATION:
+       case DESTINATION:
+       case HOST:
+       case INSTANCE:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "invalid value for attribute type");
+       }
+       if(o != WITHIN) {
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "only operator WITHIN allowed with two values");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a,
+                        const Op o,
+                        const int v1,
+                        const int v2)
+       : attr(a), oper(o), state(EDG_WLL_JOB_UNDEF), int_value(v1), int_value2(v2)
+{
+       switch(a) {
+       case DONECODE:
+       case STATUS:
+       case SOURCE:
+       case EVENT_TYPE:
+       case LEVEL:
+       case EXITCODE:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of integer type");
+       }
+       if(o != WITHIN) {
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "only operator WITHIN allowed with two values");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a,
+                        const Op o,
+                        const struct timeval &v1,
+                        const struct timeval &v2)
+       : attr(a), oper(o), state(EDG_WLL_JOB_UNDEF), timeval_value(v1), timeval_value2(v2)
+{
+       switch(a) {
+       case TIME:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of timeval type");
+       }
+       if(o != WITHIN) {
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "only operator WITHIN allowed with two values");
+       }
+}
+
+
+QueryRecord::QueryRecord(const Attr a,
+                        const Op o,
+                        const int s,
+                        const struct timeval &v1,
+                        const struct timeval &v2)
+       : attr(a), oper(o), state(s), timeval_value(v1), timeval_value2(v2)
+{
+       switch(a) {
+       case TIME:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "attribute is not of timeval type");
+       }
+       if(o != WITHIN) {
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "only operator WITHIN allowed with two values");
+       }
+}
+
+
+QueryRecord::QueryRecord(const std::string &tag,
+                        const Op o,
+                        const std::string &val)
+       : attr(USERTAG), oper(o), tag_name(tag), state(EDG_WLL_JOB_UNDEF), string_value(val)
+{
+}
+
+
+QueryRecord::QueryRecord(const std::string &tag,
+                        const Op o,
+                        const std::string &v1,
+                        const std::string &v2)
+       : attr(USERTAG), oper(o), tag_name(tag), state(EDG_WLL_JOB_UNDEF), 
+         string_value(v1), string_value2(v2)
+         
+{
+       if(o != WITHIN) {
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "only operator WITHIN allowed with two values");
+       }
+}
+
+
+QueryRecord::QueryRecord(const QueryRecord &src)
+{
+       attr = src.attr;
+       oper = src.oper;
+
+       switch (attr) {
+
+       case USERTAG:
+               tag_name = src.tag_name;
+
+       case OWNER:
+       case LOCATION:
+       case DESTINATION:
+       case HOST:
+       case INSTANCE:
+               string_value = src.string_value;
+               if(src.oper == WITHIN)
+                       string_value2 = src.string_value2;
+               break;
+
+       case DONECODE:
+       case STATUS:
+       case SOURCE:
+       case EVENT_TYPE:
+       case LEVEL:
+       case EXITCODE:
+               int_value = src.int_value;
+               if(src.oper == WITHIN)
+                       int_value2 = src.int_value2;
+               break;
+
+       case TIME:
+               timeval_value = src.timeval_value;
+               if(src.oper == WITHIN)
+                       timeval_value2 = src.timeval_value2;
+               state = src.state;
+               break;
+
+       case JOBID:
+                       jobid_value = src.jobid_value;
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "query attribute not defined");
+       }
+}
+
+
+QueryRecord::QueryRecord() : attr(UNDEF), oper(EQUAL)
+{
+}
+
+
+QueryRecord::~QueryRecord()
+{
+}
+
+
+QueryRecord&
+QueryRecord::operator=(const QueryRecord &src)
+{
+        if(this == &src)
+          return(*this);
+
+       attr = src.attr;
+       oper = src.oper;
+
+       switch (attr) {
+
+       case USERTAG:
+               tag_name = src.tag_name;
+
+       case OWNER:
+       case LOCATION:
+       case DESTINATION:
+       case HOST:
+       case INSTANCE:
+               string_value = src.string_value;
+               if(oper == WITHIN)
+                       string_value2 = src.string_value2;
+               break;
+
+       case DONECODE:
+       case STATUS:
+       case SOURCE:
+       case EVENT_TYPE:
+       case LEVEL:
+       case EXITCODE:
+               int_value = src.int_value;
+               if(oper == WITHIN)
+                       int_value2 = src.int_value2;
+               break;
+
+       case TIME:
+               timeval_value = src.timeval_value;
+               state = src.state;
+               if(oper == WITHIN)
+                       timeval_value2 = src.timeval_value2;
+               break;
+
+       case JOBID:
+                       jobid_value = src.jobid_value;
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "query attribute not defined");
+       }
+
+       return *this;
+}
+
+
+QueryRecord::operator edg_wll_QueryRec() const
+{
+       edg_wll_QueryRec out;
+
+       out.attr = edg_wll_QueryAttr(attr);
+       out.op   = edg_wll_QueryOp(oper);
+
+       switch (attr) {
+
+       case USERTAG:
+               out.attr_id.tag = strdup(tag_name.c_str());
+
+       case OWNER:
+       case LOCATION:
+       case DESTINATION:
+       case HOST:
+       case INSTANCE:
+               out.value.c = strdup(string_value.c_str());
+               if(oper == WITHIN)
+                       out.value2.c = strdup(string_value2.c_str());
+               break;
+
+
+       case DONECODE:
+       case STATUS:
+       case SOURCE:
+       case EVENT_TYPE:
+       case LEVEL:
+       case EXITCODE:
+               out.value.i = int_value;
+               if(oper == WITHIN)
+                       out.value2.i = int_value2;
+               break;
+
+       case TIME:
+               out.value.t = timeval_value;
+               out.attr_id.state = (edg_wll_JobStatCode)state;
+               if(oper == WITHIN)
+                       out.value2.t = timeval_value2;
+               break;
+
+       case JOBID:
+               out.value.j = jobid_value;
+               break;
+
+       case UNDEF:
+               break;
+
+       default:
+               STACK_ADD;
+               throw Exception(EXCEPTION_MANDATORY, EINVAL, "query attribute not defined");
+       }
+
+       return(out);
+}
+
+const std::string QueryRecord::AttrName(const QueryRecord::Attr attr)
+{
+       char    *an = edg_wll_query_attrToString(edg_wll_QueryAttr(attr));
+       std::string     ret(an);
+       free(an);
+       return ret;
+}
+
+
+/** 
+ * definitions of ServerConnection class 
+ */
+#undef CLASS_PREFIX
+#define CLASS_PREFIX "edg::workload::logging::ServerConnection::"
+
+ServerConnection::ServerConnection()
+{
+       int ret;
+       edg_wll_Context tmp_context;
+
+       if((ret=edg_wll_InitContext(&tmp_context)) < 0) {
+               STACK_ADD;
+               throw OSException(EXCEPTION_MANDATORY, ret, "initializing context");
+       }
+  
+       context = tmp_context;
+}
+
+
+ServerConnection::~ServerConnection()
+{
+       /* no exceptions should be thrown from destructors */
+       edg_wll_FreeContext(context);
+}
+
+
+/********************/
+/* BEGIN DEPRECATED */
+
+ServerConnection::ServerConnection(const std::string &in)
+{
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method deprecated");
+}
+
+
+void 
+ServerConnection::open(const std::string & in) 
+{
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method deprecated");
+}
+
+
+void 
+ServerConnection::close(void) 
+{
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method deprecated");
+}
+
+/* END DEPRECATED */
+/******************/
+
+
+void 
+ServerConnection::setQueryServer(const std::string& server, int port)
+{
+       check_result(edg_wll_SetParamString(context, 
+                                           EDG_WLL_PARAM_QUERY_SERVER,
+                                           server.c_str()),
+                    context,
+                    "setting query server address");
+       check_result(edg_wll_SetParamInt(context,
+                                        EDG_WLL_PARAM_QUERY_SERVER_PORT,
+                                        port),
+                    context,
+                    "setting query server port");
+}
+
+
+void 
+ServerConnection::setQueryTimeout(int timeout)
+{
+       check_result(edg_wll_SetParamInt(context,
+                                        EDG_WLL_PARAM_QUERY_TIMEOUT,
+                                        timeout),
+                    context,
+                    "setting query timeout");
+}
+
+
+void ServerConnection::setX509Proxy(const std::string& proxy)
+{
+       check_result(edg_wll_SetParamString(context,
+                                           EDG_WLL_PARAM_X509_PROXY,
+                                           proxy.c_str()),
+                    context,
+                    "setting X509 proxy");
+}
+
+
+void ServerConnection::setX509Cert(const std::string& cert, const std::string& key)
+{
+       check_result(edg_wll_SetParamString(context,
+                                           EDG_WLL_PARAM_X509_CERT,
+                                           cert.c_str()),
+                    context,
+                    "setting X509 certificate");
+       check_result(edg_wll_SetParamString(context,
+                                           EDG_WLL_PARAM_X509_KEY,
+                                           key.c_str()),
+                    context,
+                    "setting X509 key");
+}
+
+
+void 
+ServerConnection::setQueryEventsLimit(int max) {
+       check_result(edg_wll_SetParamInt(context,
+                                        EDG_WLL_PARAM_QUERY_EVENTS_LIMIT,
+                                        max),
+                    context,
+                    "setting query events limit");
+}
+
+void 
+ServerConnection::setQueryJobsLimit(int max) {
+       check_result(edg_wll_SetParamInt(context,
+                                        EDG_WLL_PARAM_QUERY_JOBS_LIMIT,
+                                        max),
+                    context,
+                    "setting query jobs limit");
+}
+
+
+std::pair<std::string, int>
+ServerConnection::getQueryServer() const
+{
+       /* FIXME: not implemented in C API */
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method not implemented");
+}
+
+
+int
+ServerConnection::getQueryTimeout() const
+{
+       /* FIXME: not implemented in C API */
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method not implemented");
+}
+
+
+std::string
+ServerConnection::getX509Proxy() const
+{
+       /* FIXME: not implemented in C API */
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method not implemented");
+}
+
+
+std::pair<std::string, std::string>
+ServerConnection::getX509Cert() const
+{
+       /* FIXME: not implemented in C API */
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method not implemented");
+}
+
+// static
+void freeQueryRecVector(edg_wll_QueryRec *v)
+{
+       for(; v->attr != EDG_WLL_QUERY_ATTR_UNDEF; v++)
+               edg_wll_QueryRecFree(v);
+}
+
+std::vector<std::vector<std::pair<QueryRecord::Attr,std::string> > >
+ServerConnection::getIndexedAttrs(void) {
+       edg_wll_QueryRec        **recs;
+       int     i,j;
+       std::vector<std::vector<std::pair<QueryRecord::Attr,std::string> > >    out;
+
+       check_result(edg_wll_GetIndexedAttrs(context,&recs),context,
+               "edg_wll_GetIndexedAttrs()");
+       
+       for (i=0; recs[i]; i++) {
+               std::vector<std::pair<QueryRecord::Attr,std::string> >  idx;
+               for (j=0; recs[i][j].attr; j++) {
+                       char    *s = strdup("");
+                       if (recs[i][j].attr == EDG_WLL_QUERY_ATTR_USERTAG)
+                               s = strdup(recs[i][j].attr_id.tag);
+                       else if (recs[i][j].attr == EDG_WLL_QUERY_ATTR_TIME)
+                               s = edg_wll_StatToString(recs[i][j].attr_id.state);
+                       idx.push_back(
+                               std::pair<QueryRecord::Attr,std::string>(
+                                       QueryRecord::Attr(recs[i][j].attr),s)
+                       );
+                       free(s);
+               }
+               freeQueryRecVector(recs[i]);
+               out.push_back(idx);
+       }
+       free(recs);
+       return out;
+}
+
+
+
+
+edg_wll_QueryRec *
+convertQueryVector(const std::vector<QueryRecord> &in)
+{
+       unsigned i;
+       edg_wll_QueryRec *out = new edg_wll_QueryRec[in.size() + 1];
+       QueryRecord empty;
+
+       if(out == NULL) {
+               STACK_ADD;
+               throw OSException(EXCEPTION_MANDATORY, ENOMEM, "allocating vector for conversion");
+       }
+
+       try {
+               for(i = 0; i < in.size(); i++) {
+                       out[i] = in[i];
+               }
+               out[i] = empty;
+       } catch (Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+       return(out);
+}
+
+
+edg_wll_QueryRec **
+convertQueryVectorExt(const std::vector<std::vector<QueryRecord> > &in)
+{
+       unsigned i;
+       edg_wll_QueryRec **out = new (edg_wll_QueryRec*)[in.size() + 1];
+
+       if(out == NULL) {
+               STACK_ADD;
+               throw OSException(EXCEPTION_MANDATORY, ENOMEM, "allocating vector for conversion");
+       }
+
+       try {
+               for(i = 0; i < in.size(); i++) {
+                       out[i] = convertQueryVector(in[i]);
+               }
+               out[i] = NULL;
+       } catch (Exception &e) {
+               STACK_ADD;
+               throw;
+       }
+       return(out);
+}
+
+void 
+ServerConnection::queryEvents(const std::vector<QueryRecord>& job_cond,
+                             const std::vector<QueryRecord>& event_cond,
+                             std::vector<Event> & eventList) const
+{
+       edg_wll_QueryRec    *job_rec = NULL, *event_rec = NULL;
+       edg_wll_Event       *events = NULL;
+       unsigned        i;
+       int             result, qresults_param;
+       char            *errstr = NULL;
+       
+       /* convert input */
+       try {
+               job_rec = convertQueryVector(job_cond);
+               event_rec = convertQueryVector(event_cond);
+
+               /* do the query */
+
+               result = edg_wll_QueryEvents(context, job_rec, event_rec, &events);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_QueryEvents");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_QueryEvents");
+               }
+               
+               /* convert output */
+               for (i=0; events[i].type != EDG_WLL_EVENT_UNDEF; i++) {
+                       edg_wll_Event   *ev = (edg_wll_Event *) malloc(sizeof *ev);
+                       memcpy(ev,events+i,sizeof *ev);
+                       Event   e(ev);
+      
+                       eventList.push_back(e);
+               }
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryEvents");
+               }
+    
+               free(events);
+               delete[] job_rec;
+               delete[] event_rec;
+
+       } catch(Exception &e) {
+               if(job_rec) delete[] job_rec;
+               if(event_rec) delete[] event_rec;
+               if(events) free(events);
+               if(errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+
+const std::vector<Event>  
+ServerConnection::queryEvents(const std::vector<QueryRecord>& job_cond,
+                             const std::vector<QueryRecord>& event_cond) const
+{
+       std::vector<Event>      eventList;
+
+       queryEvents(job_cond, event_cond,eventList);
+       return eventList;
+}
+
+const std::list<Event>
+ServerConnection::queryEventsList(const std::vector<QueryRecord>& job_cond,
+                       const std::vector<QueryRecord>& event_cond) const
+{
+       std::vector<Event> events;
+
+       queryEvents(job_cond, event_cond, events);
+       return std::list<Event>(events.begin(),events.end());
+}
+
+std::string 
+ServerConnection::queryEventsAggregate(const std::vector<QueryRecord>& job_cond,
+                                      const std::vector<QueryRecord>& event_cond,
+                                      enum AggOp const op,
+                                      std::string const attr) const
+{
+       STACK_ADD;
+       throw Exception(EXCEPTION_MANDATORY, 0, "method not implemented");
+       return ""; // gcc warning;
+}
+
+
+void 
+ServerConnection::queryEvents(const std::vector<std::vector<QueryRecord> >& job_cond,
+                             const std::vector<std::vector<QueryRecord> >& event_cond,
+                             std::vector<Event>& eventList) const
+{
+       edg_wll_QueryRec    **job_rec = NULL, **event_rec = NULL;
+       edg_wll_Event       *events = NULL;
+       unsigned        i;
+       
+       /* convert input */
+       try {
+               job_rec = convertQueryVectorExt(job_cond);
+               event_rec = convertQueryVectorExt(event_cond);
+
+               /* do the query */
+
+               check_result(edg_wll_QueryEventsExt(context, 
+                                                   (const edg_wll_QueryRec**)job_rec,
+                                                   (const edg_wll_QueryRec**)event_rec, 
+                                                   &events),
+                            context,
+                            "edg_wll_QueryEvents");
+    
+               /* convert output */
+               for (i=0; events[i].type != EDG_WLL_EVENT_UNDEF; i++) {
+                       edg_wll_Event   *ev = (edg_wll_Event *) malloc(sizeof *ev);
+                       memcpy(ev,events+i,sizeof *ev);
+                       Event   e(ev);
+      
+                       eventList.push_back(e);
+               }
+    
+               free(events);
+
+               for(i = 0 ; job_rec[i]; i++) delete[] job_rec[i];
+               for(i = 0 ; event_rec[i]; i++) delete[] event_rec[i];
+               delete[] job_rec;
+               delete[] event_rec;
+
+       } catch(Exception &e) {
+
+               if(job_rec) {
+                       for(i = 0 ; job_rec[i]; i++) delete[] job_rec[i];
+                       delete[] job_rec;
+               }
+               if(event_rec) { 
+                       for(i = 0 ; event_rec[i]; i++) delete[] event_rec[i];
+                       delete[] event_rec;
+               }
+               if(events) free(events);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+  
+const std::vector<Event> 
+ServerConnection::queryEvents(const std::vector<std::vector<QueryRecord> >& job_cond,
+                             const std::vector<std::vector<QueryRecord> >& event_cond) const
+{
+       std::vector<Event>      eventList;
+
+       queryEvents(job_cond, event_cond,eventList);
+       return eventList;
+}
+
+
+void ServerConnection::queryJobs(const std::vector<QueryRecord>& query,
+                                std::vector<edg::workload::common::jobid::JobId> & ids) const
+{
+       edg_wll_QueryRec *cond = NULL;
+       edg_wlc_JobId *jobs, *j;
+       int     result, qresults_param;
+       char    *errstr = NULL;
+
+       try {
+               cond = convertQueryVector(query);
+    
+               result = edg_wll_QueryJobs(context, cond, 0, &jobs, NULL);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_QueryJobs");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_QueryJobs");
+               }
+
+               for(j = jobs; *j; j++) 
+                       ids.push_back(edg::workload::common::jobid::JobId(*j));
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryJobs");
+               }
+
+               free(jobs);
+               freeQueryRecVector(cond);
+               delete[] cond;
+
+       } catch (Exception &e) {
+               if(cond) {
+                       freeQueryRecVector(cond);
+                       delete[] cond;
+               }
+               if (errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+
+const std::vector<edg::workload::common::jobid::JobId>
+ServerConnection::queryJobs(const std::vector<QueryRecord>& query) const
+{
+       std::vector<edg::workload::common::jobid::JobId> jobList;
+  
+       queryJobs(query, jobList);
+       return jobList;
+}
+
+
+void 
+ServerConnection::queryJobs(const std::vector<std::vector<QueryRecord> >& query,
+                           std::vector<edg::workload::common::jobid::JobId>& ids) const
+{
+       edg_wll_QueryRec **cond = NULL;
+       edg_wlc_JobId *jobs, *j;
+       int     result, qresults_param;
+        char    *errstr = NULL;
+
+       try {
+               cond = convertQueryVectorExt(query);
+    
+               result = edg_wll_QueryJobsExt(context, (const edg_wll_QueryRec**)cond, 
+                                              0, &jobs, NULL);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_QueryJobsExt");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_QueryJobsExt");
+               }
+
+               for(j = jobs; *j; j++) 
+                       ids.push_back(edg::workload::common::jobid::JobId(*j));
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryJobsExt");
+               }
+
+               free(jobs);
+               {
+                       unsigned i;
+
+                       for(i = 0; cond[i]; i++) {
+                               freeQueryRecVector(cond[i]);
+                               delete[] cond[i];
+                       }
+                       delete[] cond;
+               }
+
+       } catch (Exception &e) {
+               unsigned i;
+               if(cond) {
+                       for(i = 0; cond[i]; i++) {
+                               freeQueryRecVector(cond[i]);
+                               delete[] cond[i];
+                       }
+                       delete[] cond;
+               }
+               if (errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+  
+const 
+std::vector<edg::workload::common::jobid::JobId>
+ServerConnection::queryJobs(const std::vector<std::vector<QueryRecord> >& query) const
+{
+       std::vector<edg::workload::common::jobid::JobId> jobList;
+  
+       queryJobs(query, jobList);
+       return jobList;
+}
+
+
+void
+ServerConnection::queryJobStates(const std::vector<QueryRecord>& query,
+                                int flags,
+                                std::vector<JobStatus> & states) const
+{
+       edg_wll_QueryRec *cond = NULL;
+       edg_wll_JobStat *jobs, *j;
+       int     result, qresults_param;
+       char    *errstr = NULL;
+
+       try {
+               cond = convertQueryVector(query);
+    
+               result = edg_wll_QueryJobs(context, cond, flags, NULL, &jobs);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_QueryJobs");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_QueryJobs");
+               }
+
+               for(j = jobs; j->state != EDG_WLL_JOB_UNDEF; j++) {
+                       edg_wll_JobStat *jsep = new edg_wll_JobStat;
+                       if (jsep != NULL) {
+                               memcpy(jsep, j, sizeof(*j));
+                               states.push_back(JobStatus(*jsep));
+                       }
+               }
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryJobs");
+               }
+
+               delete jobs;
+
+               freeQueryRecVector(cond);
+               delete[] cond;
+
+       } catch (Exception &e) {
+               if(cond) { 
+                       freeQueryRecVector(cond);
+                       delete[] cond;
+               }
+               if (errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+
+const std::vector<JobStatus>
+ServerConnection::queryJobStates(const std::vector<QueryRecord>& query,
+                                int flags) const
+{
+       std::vector<JobStatus> states;
+
+       queryJobStates(query, flags, states);
+       return(states);
+}
+
+const std::list<JobStatus>
+ServerConnection::queryJobStatesList(const std::vector<QueryRecord>& query,
+                                int flags) const
+{
+       std::vector<JobStatus> states;
+
+       queryJobStates(query, flags, states);
+       return std::list<JobStatus>(states.begin(),states.end());
+}
+
+
+void
+ServerConnection::queryJobStates(const std::vector<std::vector<QueryRecord> >& query,
+                                int flags,
+                                std::vector<JobStatus> & states) const
+{
+       edg_wll_QueryRec **cond = NULL;
+       edg_wll_JobStat *jobs, *j;
+       int     result, qresults_param;
+       char    *errstr = NULL;
+
+       try {
+               cond = convertQueryVectorExt(query);
+    
+               result = edg_wll_QueryJobsExt(context, (const edg_wll_QueryRec**)cond, 
+                                              flags, NULL, &jobs);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_QueryJobsExt");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_QueryJobsExt");
+               }
+
+               for(j = jobs; j->state != EDG_WLL_JOB_UNDEF; j++) {
+                       edg_wll_JobStat *jsep = new edg_wll_JobStat;
+                       if (jsep != NULL) {
+                               memcpy(jsep, j, sizeof(*j));
+                               states.push_back(JobStatus(*jsep));
+                       }
+               }
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryJobsExt");
+               }
+
+               delete jobs;
+
+               {
+                       unsigned i;
+
+                       for(i = 0; cond[i]; i++) { 
+                               freeQueryRecVector(cond[i]);
+                               delete[] cond[i];
+                       }
+                       delete[] cond;
+               }
+
+
+       } catch (Exception &e) {
+               unsigned i;
+               if(cond) { 
+                       for(i = 0; cond[i]; i++) {
+                               freeQueryRecVector(cond[i]);
+                               delete[] cond[i];
+                       }
+                       delete[] cond;
+               }
+               if (errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+
+const std::vector<JobStatus>
+ServerConnection::queryJobStates(const std::vector<std::vector<QueryRecord> >& query,
+                                int flags) const
+{
+       std::vector<JobStatus> states;
+
+       queryJobStates(query, flags, states);
+       return(states);
+}
+
+
+void ServerConnection::userJobs(std::vector<edg::workload::common::jobid::JobId> & ids) const
+{
+       edg_wlc_JobId *jobs, *j;
+       int     result, qresults_param;
+       char    *errstr = NULL;
+
+       try {
+               result = edg_wll_UserJobs(context, &jobs, NULL);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_UserJobs");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_UserJobs");
+               }
+
+               for(j = jobs; *j; j++) 
+                       ids.push_back(edg::workload::common::jobid::JobId(*j));
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryJobsExt");
+               }
+
+               free(jobs);
+
+       } catch (Exception &e) {
+               if (errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+
+const std::vector<edg::workload::common::jobid::JobId>
+ServerConnection::userJobs() const
+{
+       std::vector<edg::workload::common::jobid::JobId> jobList;
+  
+       userJobs(jobList);
+       return jobList;
+}
+
+
+void
+ServerConnection::userJobStates(std::vector<JobStatus> & states) const
+{
+       edg_wll_JobStat *jobs, *j;
+       int     result, qresults_param;
+       char    *errstr = NULL;
+
+       try {
+               result = edg_wll_UserJobs(context, NULL, &jobs);
+               if (result == E2BIG) {
+                       edg_wll_Error(context, NULL, &errstr);
+                       check_result(edg_wll_GetParam(context,
+                                               EDG_WLL_PARAM_QUERY_RESULTS, &qresults_param),
+                                       context,
+                                       "edg_wll_GetParam(EDG_WLL_PARAM_QUERY_RESULTS)");
+                       if (qresults_param != EDG_WLL_QUERYRES_LIMITED) {
+                               edg_wll_SetError(context, result, errstr);
+                               check_result(result, context,"edg_wll_UserJobs");
+                       }
+               } else {
+                       check_result(result, context,"edg_wll_UserJobs");
+               }
+
+               for(j = jobs; j->state != EDG_WLL_JOB_UNDEF; j++) {
+                       edg_wll_JobStat *jsep = new edg_wll_JobStat;
+                       if (jsep != NULL) {
+                               memcpy(jsep, j, sizeof(*j));
+                               states.push_back(JobStatus(*jsep));
+                       }
+               }
+
+               if (result) {
+                       edg_wll_SetError(context, result, errstr);
+                       check_result(result, context,"edg_wll_QueryJobsExt");
+               }
+
+               delete jobs;
+
+       } catch (Exception &e) {
+               if (errstr) free(errstr);
+
+               STACK_ADD;
+               throw;
+       }
+}
+
+
+const std::vector<JobStatus>
+ServerConnection::userJobStates() const
+{
+       std::vector<JobStatus> states;
+
+       userJobStates(states);
+       return(states);
+}
+
+
+edg_wll_Context
+ServerConnection::getContext(void) const
+{
+       return(context);
+}
+
+
+void ServerConnection::setParam(edg_wll_ContextParam par, int val)
+{
+       check_result(edg_wll_SetParamInt(context,par,val),
+               context,
+               "edg_wll_SetParamInt()");
+}
+
+void ServerConnection::setParam(edg_wll_ContextParam par, const std::string val)
+{
+       check_result(edg_wll_SetParamString(context,par,val.c_str()),
+               context,
+               "edg_wll_SetParamString()");
+}
+
+void ServerConnection::setParam(edg_wll_ContextParam par, const struct timeval & val)
+{
+       check_result(edg_wll_SetParamTime(context,par,&val),
+               context,
+               "edg_wll_SetParamTime()");
+}
+
+int ServerConnection::getParamInt(edg_wll_ContextParam par) const
+{
+       int     ret;
+       check_result(edg_wll_GetParam(context,par,&ret),
+               context,
+               "edg_wll_GetParam()");
+       return  ret;
+}
+
+std::string ServerConnection::getParamString(edg_wll_ContextParam par) const
+{
+       char    *ret;
+       std::string     out;
+
+       check_result(edg_wll_GetParam(context,par,&ret),
+               context,
+                "edg_wll_GetParam()");
+
+       out = ret;
+       free(ret);
+       return out;
+}
+
+struct timeval ServerConnection::getParamTime(edg_wll_ContextParam par) const
+{
+       struct timeval  ret;
+       check_result(edg_wll_GetParam(context,par,&ret),
+               context,
+               "edg_wll_GetParam()");
+       return ret;
+}
+
+EWL_END_NAMESPACE;
diff --git a/org.glite.lb.client/src/args.c.T b/org.glite.lb.client/src/args.c.T
new file mode 100644 (file)
index 0000000..20defdf
--- /dev/null
@@ -0,0 +1,533 @@
+/*
+@@@AUTO
+*/
+
+@@@LANG: C
+
+#include "args.h"
+#include "glite/lb/events.h"
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/utsname.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+
+typedef struct {
+    int argc;
+    char** argv;
+    const char* help;
+    int idx;
+    edg_wll_Args list[1000];
+    int last;
+} opt_ctx_t;
+
+/* lists of accepted tags */
+@@@{
+                gen "static const char * const eventJobCommon\[] = {";
+                selectType $event '_common_';
+                for (getFieldsOrdered $event) {
+                        my $f = selectField $event $_;
+                        my $fn = $f->{name};
+                        gen "\"$fn\",  ";
+                }
+                gen "NULL };\n";
+@@@}
+@@@{
+        for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+                        $event->getTypes) {
+                gen "static const char * const event$t\[] = {";
+                selectType $event $t;
+                for (getFieldsOrdered $event) {
+                        my $f = selectField $event $_;
+                        my $fn = $f->{name};
+                        gen "\"$fn\",  "
+                }
+                gen "NULL };\n";
+        }
+@@@}
+
+static const char * const * const eventTags[] = {
+        eventJobCommon,
+@@@{
+        for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+                        $event->getTypes) {
+                gen "\tevent$t,\n";
+        }
+        gen "\tNULL\n};\n";
+@@@}
+
+static int read_bool(const edg_wll_Args* o, char* arg, char* par)
+{
+    int b = 1;
+    int rs = 0;
+
+    if (par)
+    {
+       if (!strcasecmp(par, "off") || !strcmp(par, "0")
+           || !strcasecmp(par, "false"))
+       {
+           b = 0;
+           rs = 1;
+       }
+       else if (!strcasecmp(par, "on") || !strcmp(par, "1")
+                || !strcasecmp(par, "true"))
+       {
+           rs = 1;
+       }
+    }
+
+    if (o->value)
+       *(int*)o->value = b;
+    return rs;
+}
+
+static void read_double(const edg_wll_Args* o, char* arg, char* par)
+{
+    double d;
+    if (!par)
+    {
+       printf("Option: %s  - missing double/float value\n", arg);
+       exit(1);
+    }
+    d = atof(par);
+    if (o->min != o->max)
+    {
+       if (d < o->min && d > o->max)
+       {
+           printf("Option: %s  - value: %f  out of range <%d, %d>",
+                  arg, d, o->min, o->max);
+           exit(1);
+       }
+    }
+    if (o->value)
+       *(double*)o->value = d;
+}
+
+static void read_string(const edg_wll_Args* o, char* arg, char* par)
+{
+    if (!par)
+    {
+       printf("Option: %s  - missing string value\n", arg);
+       exit(1);
+    }
+    if (o->value)
+    {
+       if (strstr(arg, "-file"))
+       {
+            struct stat buf;
+           int fd = open(par, O_RDONLY);
+           if (fd >= 0)
+           {
+                char* b;
+               fstat(fd, &buf);
+               b = (char*) malloc(buf.st_size);
+               if (b)
+               {
+                    int p = 0;
+                   printf("Opened & reading %s  %lld\n",
+                               par, (long long)buf.st_size);
+                   while (p < buf.st_size)
+                   {
+                       int r = read(fd, b + p, buf.st_size);
+                       if (r < 0)
+                           break;
+                       p += r;
+                   }
+
+                   *(char**)o->value = b;
+               }
+                close(fd);
+           }
+            else
+               fprintf(stderr, "can't open file: \"%s\"  (%s)\n", par, strerror(errno));
+
+       }
+       else
+           *(char**)o->value = strdup(par);
+    }
+}
+
+static void read_int(const edg_wll_Args* o, char* arg, char* par)
+{
+    int v = 0;
+    if (!par)
+    {
+       printf("Option: %s  - missing integer value\n", arg);
+       exit(1);
+    }
+    sscanf(par, "%i", &v);
+    if (o->min != o->max)
+    {
+       if (v < o->min && v > o->max)
+       {
+           printf("Option: %s  - value: %d  out of range <%d, %d>",
+                  arg, v, o->min, o->max);
+           exit(1);
+       }
+    }
+    if (o->value)
+       *(int*)o->value = v;
+}
+
+static void read_uint16(const edg_wll_Args* o, char* arg, char* par)
+{
+    int v = 0;
+    if (!par)
+    {
+       printf("Option: %s  - missing integer value\n", arg);
+       exit(1);
+    }
+    sscanf(par, "%i", &v);
+    if (o->min != o->max)
+    {
+       if (v < o->min && v > o->max)
+       {
+           printf("Option: %s  - value: %d  out of range <%d, %d>",
+                  arg, v, o->min, o->max);
+           exit(1);
+       }
+    }
+    if (o->value)
+       *(u_int16_t*)o->value = v;
+}
+
+static void read_event(const edg_wll_Args* o, char* arg, char* par)
+{
+    edg_wll_EventCode ec = edg_wll_StringToEvent(par);
+    if (ec == EDG_WLL_EVENT_UNDEF)
+    {
+       if (strcmp(par, "help") == 0)
+       {
+           // list type
+           int i;
+           printf("Available events:   extra options\n");
+           for (i = EDG_WLL_EVENT_UNDEF + 1; i < EDG_WLL_EVENT__LAST; i++)
+           {
+               char* e = edg_wll_EventToString(i);
+               if (e)
+               {
+                    int j = 0;
+                   printf("  %s:  ", e);
+                   while (eventTags[i][j])
+                       printf("%s ", eventTags[i][j++]);
+                    fputc('\n', stdout);
+
+                   free(e);
+               }
+           }
+       }
+        else
+           fprintf(stderr,"ERROR %s  unknown event: %s\n", arg, par);
+       exit(1);
+    }
+    if (o->value)
+       *(edg_wll_EventCode*)o->value = ec;
+
+}
+
+static void read_source(const edg_wll_Args* o, char* arg, char* par)
+{
+    edg_wll_Source s = edg_wll_StringToSource(par);
+    if (s == EDG_WLL_SOURCE_NONE)
+    {
+       if (strcmp(par, "help") == 0)
+       {
+           // list type
+           int i;
+           printf("Valid sources:\n");
+           for (i = EDG_WLL_SOURCE_NONE + 1; i < EDG_WLL_SOURCE__LAST; i++)
+           {
+               char* e = edg_wll_SourceToString(i);
+               if (e)
+               {
+                   printf("  %s\n", e);
+                           free(e);
+               }
+           }
+       }
+       else
+           fprintf(stderr,"ERROR %s  unknown source: %s\n", arg, par);
+       exit(1);
+    }
+    if (o->value)
+       *(edg_wll_Source*)o->value = s;
+}
+
+static void show_help(const edg_wll_Args* o, int prefix)
+{
+    unsigned max = 0;
+    char** l = malloc(sizeof(char*) * 1000);
+    int li = 0;
+    unsigned i = 0;
+
+    for (i = 0; o[i].type != EDG_WLL_ARGS_NONE; i++)
+    {
+       char b[80];
+        unsigned len;
+       if (o[i].type == EDG_WLL_ARGS_HELP)
+           sprintf(b, "  -h  --help");
+       else
+           sprintf(b, "  %c%s  %s%s",
+                   (o[i].oshort && prefix) ? '-' : ' ',
+                   o[i].oshort ? o[i].oshort : " ",
+                   (o[i].olong && prefix) ? "--" : "",
+                   o[i].olong ? o[i].olong : ""
+                   //opt[i].options ? opt[i].options : "",
+                   //o[i].help ? o[i].help : ""
+                  );
+       l[li] = strdup(b);
+        li++;
+       len = strlen(b);
+       if (max < len)
+           max = len;
+    }
+    for (i = 0; o[i].type != EDG_WLL_ARGS_NONE; i++)
+    {
+       if (!o[i].oshort && !o[i].olong
+           && o[i].type != EDG_WLL_ARGS_HELP
+           && o[i].type != EDG_WLL_ARGS_OPTIONS)
+            continue;
+
+       if (o[i].type != EDG_WLL_ARGS_OPTIONS)
+       {
+            unsigned s;
+           fputs(l[i], stdout);
+           for (s = strlen(l[i]); s <= max; s++)
+               fputc(' ', stdout);
+           if (o[i].type == EDG_WLL_ARGS_HELP)
+               fputs("this help message", stdout);
+       }
+
+       if (o[i].value)
+       {
+           switch (o[i].type)
+           {
+           case EDG_WLL_ARGS_INT:
+               if (o[i].help)
+                   printf(o[i].help, *(int*)o[i].value,
+                          o[i].min, o[i].max);
+               break;
+           case EDG_WLL_ARGS_STRING:
+           case EDG_WLL_ARGS_SELECTSTRING:
+                if (o[i].help)
+                   printf(o[i].help, *(const char**)o[i].value);
+               break;
+           case EDG_WLL_ARGS_OPTIONS:
+               show_help((const edg_wll_Args*)o[i].value, prefix);
+               continue;
+           default:
+                if (o[i].help)
+                   fputs(o[i].help, stdout);
+               break;
+           }
+       }
+       else if (o[i].help)
+           fputs(o[i].help, stdout);
+
+       fputs("\n", stdout);
+    }
+    while (--li>=0) free(l[li]);
+    free(l);
+}
+#if 0
+static void parse_suboptions(const Option* o, const char* oname, char* pars, const char* r)
+{
+    avm::vector<char*> arr;
+    split(arr, pars);
+
+    if (!arr.size() || strcmp(arr[0], "help") == 0)
+    {
+       printf("Available options for '%s' (optA=x:optB=...)\n", oname);
+       show_help(o, false);
+       exit(0);
+    }
+    for (unsigned i = 0; i < arr.size(); i++)
+    {
+       char* par = strchr(arr[i], '=');
+       if (par)
+       {
+           *par = 0;
+           par++;
+       }
+
+       for (unsigned j = 0; o[j].type != Args::Option::NONE; j++)
+       {
+           if ((o[j].oshort && strcmp(o[j].oshort, arr[i]) == 0)
+               || (o[j].olong && strcmp(o[j].olong, arr[i]) == 0))
+           {
+               switch(o[j].type)
+               {
+               case Args::Option::BOOL:
+                   read_bool(&o[j], arr[i], par, r);
+                   break;
+               case Args::Option::DOUBLE:
+                   read_double(&o[j], arr[i], par, r);
+                   break;
+               case Args::Option::INT:
+                   read_int(&o[j], arr[i], par, r);
+                   break;
+               default:
+                   ;
+               }
+           }
+       }
+    }
+}
+#endif
+
+static int findOpt(opt_ctx_t* ctx, int olong)
+{
+    char* arg = ctx->argv[ctx->idx] + olong + 1;
+    char* par = strchr(arg, '=');
+    const edg_wll_Args* o = NULL;
+    const edg_wll_Args* ol[200];
+    int olb = 0;
+    int ole = 0;
+
+    char* argnofile = strdup(arg);
+    char* nofile = strstr(argnofile, "-file"); // should be the ending
+    if (nofile && (nofile - argnofile) > 2)
+       nofile[0] = 0;
+
+    olong++;
+    if (par)
+    {
+        *par = 0;
+       par++;
+    }
+    else if ((ctx->idx + 1) < ctx->argc)
+    {
+       par = ctx->argv[++ctx->idx];
+    }
+
+    ol[ole++] = ctx->list;
+    while (ole > olb)
+    {
+       o = ol[olb++];
+       for (; o->type != EDG_WLL_ARGS_NONE; o++)
+       {
+           //printf("OPTION %d   '%s'  %d   '%s' '%s'\n", o->type, arg, olong, o->oshort, o->olong);
+           if (o->type == EDG_WLL_ARGS_HELP
+               && (strcasecmp(arg, "h") == 0
+                   || strcmp(arg, "?") == 0
+                   || strcasecmp(arg, "help") == 0))
+               break;
+           if (o->type == EDG_WLL_ARGS_OPTIONS)
+           {
+                ol[ole++] = o->value;
+                continue;
+           }
+
+           if (olong < 2)
+           {
+               if (o->oshort &&
+                   (strcmp(arg, o->oshort) == 0
+                    || strcmp(argnofile, o->oshort) == 0))
+                   break;
+           }
+           else if (o->olong && (strcmp(arg, o->olong) == 0
+                                 || strcmp(argnofile, o->olong) == 0))
+               break;
+       }
+       if (o->type != EDG_WLL_ARGS_NONE)
+           break;
+    }
+
+    switch (o->type)
+    {
+    case EDG_WLL_ARGS_NONE:
+       return -1;
+    case EDG_WLL_ARGS_BOOL:
+       if (!read_bool(o, arg, par))
+           ctx->idx--; // no argument given
+       break;
+    case EDG_WLL_ARGS_DOUBLE:
+       read_double(o, arg, par);
+       break;
+    case EDG_WLL_ARGS_STRING:
+        read_string(o, arg, par);
+        break;
+    case EDG_WLL_ARGS_INT:
+       read_int(o, arg, par);
+       break;
+    case EDG_WLL_ARGS_UINT16:
+       read_uint16(o, arg, par);
+       break;
+    case EDG_WLL_ARGS_EVENT:
+       read_event(o, arg, par);
+       break;
+    case EDG_WLL_ARGS_SOURCE:
+       read_source(o, arg, par);
+       break;
+    case EDG_WLL_ARGS_HELP:
+       printf("\nUsage: %s %s\n\n", ctx->argv[0], ctx->help);
+       show_help(ctx->list, 1);
+       exit(0);
+    //case EDG_WLL_ARGS_SUBOPTIONS:
+       //parse_suboptions((const edg_wll_Args*)o->value, arg, par, regname);
+    default:
+       printf("FIXME: unhandle option type %d\n", o->type);
+        break;
+    }
+
+    if (argnofile)
+        free(argnofile);
+
+    return 0;
+}
+
+static void addOptions(opt_ctx_t* ctx, const edg_wll_Args* options)
+{
+    const edg_wll_Args* o = options;
+
+    while (o->type != EDG_WLL_ARGS_NONE)
+    {
+       ctx->list[ctx->last++] = *o;
+        o++;
+    }
+}
+
+void edg_wll_ParseArgs(int* argc, char** argv, const edg_wll_Args* options,
+                      const char* help)
+{
+    int sidx = 1;
+    opt_ctx_t ctx;
+
+    ctx.argc = *argc;
+    ctx.argv = argv;
+    ctx.help = help;
+    ctx.last = 0;
+
+    addOptions(&ctx, options);
+
+    for (ctx.idx = 1; ctx.idx < ctx.argc; ctx.idx++)
+    {
+       if (argv[ctx.idx][0] == '-')
+       {
+           int olong = (argv[ctx.idx][1] == '-');
+           if (olong && argv[ctx.idx][2] == 0)
+               break; // end of options
+           //printf("ARG %d  %s\n", ctx.idx, argv[ctx.idx]);
+           if (findOpt(&ctx, olong) == 0)
+               continue;
+       }
+       else if (sidx != ctx.idx)
+       {
+           fprintf(stderr,"SIDX  %d  %d\n", sidx, ctx.idx);
+           argv[sidx] = argv[ctx.idx];
+       }
+       sidx++;
+    }
+
+    while (ctx.idx < *argc && sidx != ctx.idx)
+    {
+       argv[sidx++] = argv[ctx.idx++];
+    }
+
+    *argc = sidx;
+}
diff --git a/org.glite.lb.client/src/args.h b/org.glite.lb.client/src/args.h
new file mode 100644 (file)
index 0000000..6daaf4e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_ARGS_H__
+
+typedef enum {
+    EDG_WLL_ARGS_NONE = 0,
+    EDG_WLL_ARGS_BOOL,
+    EDG_WLL_ARGS_INT,
+    EDG_WLL_ARGS_UINT16,
+    EDG_WLL_ARGS_DOUBLE,
+    EDG_WLL_ARGS_STRING,
+    EDG_WLL_ARGS_HELP,
+    EDG_WLL_ARGS_JOBID,
+    EDG_WLL_ARGS_NOTIFID,
+    EDG_WLL_ARGS_SOURCE,
+    EDG_WLL_ARGS_EVENT,
+    EDG_WLL_ARGS_OPTIONS,
+    EDG_WLL_ARGS_SUBOPTIONS,
+    EDG_WLL_ARGS_SELECTSTRING,
+} edg_wll_ArgsCode;
+
+typedef struct {
+    edg_wll_ArgsCode type;
+    const char* oshort;
+    const char* olong;
+    const char* help;
+    void* value;
+    int min;
+    int max;
+} edg_wll_Args;
+
+void edg_wll_ParseArgs(int* argc, char** argv, const edg_wll_Args* parray,
+                       const char* help);
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_ARGS_H__ */
diff --git a/org.glite.lb.client/src/connection.c b/org.glite.lb.client/src/connection.c
new file mode 100644 (file)
index 0000000..2e09ac7
--- /dev/null
@@ -0,0 +1,263 @@
+#ident "$Header$"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include "globus_config.h"
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/dgssl.h"
+#include "glite/lb/mini_http.h"
+
+
+
+static void CloseConnection(edg_wll_Context ctx, int conn_index)
+{
+       /* close connection ad free its structures */
+       if (ctx->connPool[conn_index].ssl) 
+               edg_wll_ssl_close_timeout(ctx->connPool[conn_index].ssl,&ctx->p_tmp_timeout);
+       if (ctx->connPool[conn_index].gsiCred) 
+               edg_wll_ssl_free(ctx->connPool[conn_index].gsiCred);
+       free(ctx->connPool[conn_index].peerName);
+       free(ctx->connPool[conn_index].buf);
+       
+       memset(ctx->connPool + conn_index, 0, sizeof(edg_wll_ConnPool));
+       
+       /* if deleted conn was not the last one -> there is a 'hole' and then   */
+       /* 'shake' together connections in pool, no holes are allowed           */
+       if (conn_index < ctx->connOpened - 1) { 
+               ctx->connPool[conn_index] = ctx->connPool[ctx->connOpened - 1];
+               memset(ctx->connPool + ctx->connOpened  - 1 , 0, sizeof(edg_wll_ConnPool));
+       }
+       ctx->connOpened--;
+}
+
+
+
+static int ConnectionIndex(edg_wll_Context ctx, const char *name, int port)
+{
+       int i;
+
+        for (i=0; i<ctx->connOpened;i++) 
+               if (!strcmp(name, ctx->connPool[i].peerName) &&
+                   (port == ctx->connPool[i].peerPort)) return i;
+                                               
+       return -1;
+}
+
+
+
+static int AddConnection(edg_wll_Context ctx, char *name, int port)
+{
+       int index = ctx->connOpened;
+       
+       free(ctx->connPool[index].peerName);    // should be empty; just to be sure
+       ctx->connPool[index].peerName = strdup(ctx->srvName);
+       ctx->connPool[index].peerPort = ctx->srvPort;
+       ctx->connOpened++;
+
+       return index;
+}
+
+
+
+static void ReleaseConnection(edg_wll_Context ctx, char *name, int port)
+{
+       int i, index = 0;
+       long min;
+
+
+       if (ctx->connOpened == 0) return;       /* nothing to release */
+       
+       if (name) {
+               if ((index = ConnectionIndex(ctx, name, port)) >= 0)
+                       CloseConnection(ctx, index);
+       }
+       else {                                  /* free the oldest connection*/
+               min = ctx->connPool[0].lastUsed.tv_sec;
+               for (i=0; i<ctx->connOpened; i++) {
+                       if (ctx->connPool[i].lastUsed.tv_sec < min) {
+                               min = ctx->connPool[i].lastUsed.tv_sec;
+                               index = i;
+                       }
+               }
+               CloseConnection(ctx, index);
+       }
+}
+                       
+
+
+
+int edg_wll_close(edg_wll_Context ctx)
+{
+       edg_wll_ResetError(ctx);
+
+       CloseConnection(ctx, ctx->connToUse);
+               
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_open(edg_wll_Context ctx)
+{
+       int index;
+       
+
+       edg_wll_ResetError(ctx);
+
+       if ( (index = ConnectionIndex(ctx, ctx->srvName, ctx->srvPort)) == -1 ) {
+               /* no such open connection in pool */
+               if (ctx->connOpened == ctx->poolSize)
+                       ReleaseConnection(ctx, NULL, 0);
+               
+               index = AddConnection(ctx, ctx->srvName, ctx->srvPort);
+               
+       }
+       /* else - there is cached open connection, reuse it */
+       
+       ctx->connToUse = index;
+       
+       if (!ctx->connPool[index].gsiCred) { 
+               if (!(ctx->connPool[index].gsiCred = edg_wll_ssl_init(SSL_VERIFY_PEER,0,
+                       ctx->p_proxy_filename ? ctx->p_proxy_filename : ctx->p_cert_filename,
+                       ctx->p_proxy_filename ? ctx->p_proxy_filename : ctx->p_key_filename,
+                       0, 0)))
+               {
+                       edg_wll_SetError(ctx,EDG_WLL_ERROR_SSL,
+                               ERR_error_string(ERR_get_error(), NULL));
+                       goto err;
+               }
+               
+       }
+
+       if (!ctx->connPool[index].ssl) {        
+               switch (edg_wll_ssl_connect(ctx->connPool[index].gsiCred,
+                               ctx->connPool[index].peerName, ctx->connPool[index].peerPort,
+                               &ctx->p_tmp_timeout,&ctx->connPool[index].ssl)) {
+               
+                       case EDG_WLL_SSL_OK: 
+                               goto ok;
+                       case EDG_WLL_SSL_ERROR_ERRNO:
+                               edg_wll_SetError(ctx,errno,"edg_wll_ssl_connect()");
+                               break;
+                       case EDG_WLL_SSL_ERROR_SSL:
+                               edg_wll_SetError(ctx,EDG_WLL_ERROR_SSL,
+                                               ERR_error_string(ERR_get_error(), NULL));
+                               break;
+                       case EDG_WLL_SSL_ERROR_HERRNO:
+                               { const char *msg1;
+                                 char *msg2;
+                                 msg1 = hstrerror(errno);
+                                 asprintf(&msg2, "edg_wll_ssl_connect(): %s", msg1);
+                                 edg_wll_SetError(ctx,EDG_WLL_ERROR_DNS, msg2);
+                                 free(msg2);
+                               }
+                               break;
+                       case EDG_WLL_SSL_ERROR_EOF:
+                               edg_wll_SetError(ctx,ECONNREFUSED,"edg_wll_ssl_connect():"
+                                              " server closed the connection, probably due to overload");
+                               break;
+                       case EDG_WLL_SSL_ERROR_TIMEOUT:
+                               edg_wll_SetError(ctx,ETIMEDOUT,"edg_wll_ssl_connect()");
+                               break;
+               }
+       }
+       else goto ok;
+
+err:
+       /* some error occured; close created connection
+        * and free all fields in connPool[index] */
+       CloseConnection(ctx, index);
+ok:    
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+/* transform HTTP error code to ours */
+int http_check_status(
+       edg_wll_Context ctx,
+       char *response)
+
+{
+       int     code,len;
+
+       edg_wll_ResetError(ctx);
+       sscanf(response,"HTTP/%*f %n%d",&len,&code);
+       switch (code) {
+               case HTTP_OK: 
+                       break;
+               /* soft errors - some useful data may be returned too */
+               case HTTP_UNAUTH: /* EPERM */
+               case HTTP_NOTFOUND: /* ENOENT */
+               case HTTP_NOTIMPL: /* ENOSYS */
+               case HTTP_UNAVAIL: /* EAGAIN */
+               case HTTP_INVALID: /* EINVAL */
+                       break;
+               case EDG_WLL_SSL_ERROR_HERRNO:
+                        { const char *msg1;
+                          char *msg2;
+                          msg1 = hstrerror(errno);
+                          asprintf(&msg2, "edg_wll_ssl_connect(): %s", msg1);
+                          edg_wll_SetError(ctx,EDG_WLL_ERROR_DNS, msg2);
+                          free(msg2);
+                        }
+                       break;
+               case HTTP_NOTALLOWED:
+                       edg_wll_SetError(ctx, ENXIO, "Method Not Allowed");
+                       break;
+               case HTTP_UNSUPPORTED:
+                       edg_wll_SetError(ctx, ENOTSUP, "Protocol versions incompatible");
+                       break;                                                          
+               case HTTP_INTERNAL:
+                       /* fall through */
+               default: 
+                       edg_wll_SetError(ctx,EDG_WLL_ERROR_SERVER_RESPONSE,response+len);
+       }
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_http_send_recv(
+       edg_wll_Context ctx,
+       char *request,
+       const char * const *req_head,
+       char *req_body,
+       char **response,
+       char ***resp_head,
+       char **resp_body)
+{
+       if (edg_wll_open(ctx)) return edg_wll_Error(ctx,NULL,NULL);
+       
+       switch (edg_wll_http_send(ctx,request,req_head,req_body)) {
+               case ENOTCONN:
+                       edg_wll_close(ctx);
+                       if (edg_wll_open(ctx)
+                               || edg_wll_http_send(ctx,request,req_head,req_body))
+                                       return edg_wll_Error(ctx,NULL,NULL);
+                       /* fallthrough */
+               case 0: break;
+               default: return edg_wll_Error(ctx,NULL,NULL);
+       }
+
+       if (edg_wll_http_recv(ctx,response,resp_head,resp_body) == ENOTCONN) {
+               edg_wll_close(ctx);
+               (void) (edg_wll_open(ctx)
+                       || edg_wll_http_send(ctx,request,req_head,req_body)
+                       || edg_wll_http_recv(ctx,response,resp_head,resp_body));
+       }
+       
+       gettimeofday(&ctx->connPool[ctx->connToUse].lastUsed, NULL);
+       
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.client/src/connection.h b/org.glite.lb.client/src/connection.h
new file mode 100644 (file)
index 0000000..99dd933
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_CONNECTION_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_CONNECTION_H__
+
+#ident "$Header"
+
+int edg_wll_close(edg_wll_Context ctx);
+int edg_wll_open(edg_wll_Context ctx);
+int edg_wll_http_send_recv(edg_wll_Context, char *, const char * const *, char *, char **, char ***, char **);
+int http_check_status(edg_wll_Context, char *);
+
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_CONNECTION_H__ */
diff --git a/org.glite.lb.client/src/consumer.c b/org.glite.lb.client/src/consumer.c
new file mode 100644 (file)
index 0000000..d94dbbc
--- /dev/null
@@ -0,0 +1,429 @@
+#ident "$Header$"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <expat.h>
+
+#include "globus_config.h"
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/mini_http.h"
+#include "glite/lb/xml_parse.h"
+#include "glite/lb/xml_conversions.h"
+
+#include "connection.h"
+
+static const char* const request_headers[] = {
+       "Cache-Control: no-cache",
+       "Accept: application/x-dglb",
+       "User-Agent: edg_wll_Api/" PROTO_VERSION "/" COMP_PROTO,
+       "Content-Type: application/x-dglb",
+       NULL
+};
+
+int set_server_name_and_port(edg_wll_Context, const edg_wll_QueryRec **);
+
+int edg_wll_QueryEventsExt(
+               edg_wll_Context ctx,
+               const edg_wll_QueryRec **job_conditions,
+               const edg_wll_QueryRec **event_conditions,
+               edg_wll_Event **eventsOut)
+{
+       int     error           = 0;
+       char    *response       = NULL,
+               *message        = NULL,
+               *send_mess      = NULL;
+
+       edg_wll_ResetError(ctx);
+
+       if ( edg_wll_QueryEventsRequestToXML(ctx, job_conditions, event_conditions, &send_mess) != 0 )
+       {
+               edg_wll_SetError(ctx , (edg_wll_ErrorCode) EINVAL, "Invalid query record.");
+               goto err;
+       }
+
+       if ((error = set_server_name_and_port(ctx,job_conditions)))
+               goto err; // XXX is it fatal??
+
+       ctx->p_tmp_timeout = ctx->p_query_timeout;
+       error = edg_wll_http_send_recv(ctx, "POST /queryEvents HTTP/1.1",request_headers,send_mess,
+                                                                 &response,NULL,&message);
+       if ( error != 0 )
+               goto err;
+
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseQueryEvents(ctx,message,eventsOut);
+
+err:
+       free(response);
+       free(message);
+       free(send_mess);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_QueryEvents(
+               edg_wll_Context ctx,
+               const edg_wll_QueryRec *job_conditions,
+               const edg_wll_QueryRec *event_conditions,
+               edg_wll_Event **eventsOut)
+{
+       edg_wll_QueryRec        **jconds = NULL,
+                               **econds = NULL;
+       int                     i,
+                               njconds, neconds,
+                               ret;
+
+       if ( job_conditions )
+       {
+               for ( njconds = 0; job_conditions[njconds].attr != EDG_WLL_QUERY_ATTR_UNDEF ; njconds++ )
+                       ;
+               jconds = (edg_wll_QueryRec **) calloc(njconds+1, sizeof(edg_wll_QueryRec *));
+               for ( i = 0; i < njconds; i++ )
+               {
+                       jconds[i] = (edg_wll_QueryRec *) calloc(2, sizeof(edg_wll_QueryRec));
+                       jconds[i][0] = job_conditions[i];
+               }
+       }
+
+       if ( event_conditions )
+       {
+               for ( neconds = 0; event_conditions[neconds].attr != EDG_WLL_QUERY_ATTR_UNDEF ; neconds++ )
+                       ;
+               econds = (edg_wll_QueryRec **) calloc(neconds+1, sizeof(edg_wll_QueryRec *));
+               for ( i = 0; i < neconds; i++ )
+               {
+                       econds[i] = (edg_wll_QueryRec *) calloc(2, sizeof(edg_wll_QueryRec));
+                       econds[i][0] = event_conditions[i];
+               }
+       }
+
+       if ( econds && jconds )
+               ret = edg_wll_QueryEventsExt(ctx, (const edg_wll_QueryRec **) jconds, 
+                                   (const edg_wll_QueryRec **) econds, eventsOut);
+       if ( econds && !jconds )
+               ret = edg_wll_QueryEventsExt(ctx, NULL, (const edg_wll_QueryRec **) econds, eventsOut);
+       if ( !econds && jconds )
+               ret = edg_wll_QueryEventsExt(ctx, (const edg_wll_QueryRec **) jconds, NULL, eventsOut);
+       if ( !econds && !jconds )
+               ret = edg_wll_QueryEventsExt(ctx, NULL, NULL, eventsOut);
+
+       if ( jconds )
+       {
+               for ( i = 0; i < njconds ; i++ )
+                       free(jconds[i]);
+               free(jconds);
+       }
+       if ( econds )
+       {
+               for ( i = 0; i < neconds ; i++ )
+                       free(econds[i]);
+               free(econds);
+       }
+
+       return ret;
+}
+
+
+int edg_wll_QueryJobsExt(
+               edg_wll_Context         ctx,
+               const edg_wll_QueryRec **        conditions,
+               int                     flags,
+               edg_wlc_JobId **        jobsOut,
+               edg_wll_JobStat **      statesOut)
+{
+       char                    *response = NULL, *message = NULL, *send_mess = NULL;
+       
+       edg_wll_ResetError(ctx);
+
+       if (!jobsOut)   flags |= EDG_WLL_STAT_NO_JOBS;
+       if (!statesOut) {flags = 0; flags |= EDG_WLL_STAT_NO_STATES;}
+       if (edg_wll_QueryJobsRequestToXML(ctx, conditions, flags, &send_mess) != 0) {
+               edg_wll_SetError(ctx , (edg_wll_ErrorCode) EINVAL, "Invalid query record.");
+               goto err;
+       }
+
+       if (set_server_name_and_port(ctx, conditions))
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_query_timeout;
+
+       if (edg_wll_http_send_recv(ctx, "POST /queryJobs HTTP/1.1",request_headers,send_mess,
+                       &response,NULL,&message)) 
+               goto err;
+         
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseQueryJobs(ctx,message,jobsOut,statesOut);
+
+err:
+       free(response);
+       free(message);
+       free(send_mess);
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_QueryJobs(
+               edg_wll_Context         ctx,
+               const edg_wll_QueryRec *        conditions,
+               int                     flags,
+               edg_wlc_JobId **        jobsOut,
+               edg_wll_JobStat **      statesOut)
+{
+       edg_wll_QueryRec        **conds;
+       int                     i, nconds, ret;
+
+       if ( !conditions )
+               return edg_wll_QueryJobsExt(ctx, NULL, flags, jobsOut, statesOut);
+
+       for ( nconds = 0; conditions[nconds].attr != EDG_WLL_QUERY_ATTR_UNDEF ; nconds++ )
+               ;
+       conds = (edg_wll_QueryRec **) malloc((nconds+1) * sizeof(edg_wll_QueryRec *));
+       conds[nconds] = NULL;
+       for ( i = 0; i < nconds ; i++ )
+       {
+               conds[i] = (edg_wll_QueryRec *) malloc(2 * sizeof(edg_wll_QueryRec));
+               conds[i][0] = conditions[i];
+               conds[i][1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+       }
+
+       ret = edg_wll_QueryJobsExt(ctx, (const edg_wll_QueryRec **) conds, flags, jobsOut, statesOut);
+
+       for ( i = 0; i < nconds ; i++ )
+               free(conds[i]);
+       free(conds);
+               
+
+       return ret;
+}
+
+
+
+int edg_wll_GetIndexedAttrs(
+        edg_wll_Context         ctx,
+        edg_wll_QueryRec       ***attrs)
+{      
+       char                    *response = NULL, *send_mess = NULL, *message = NULL;
+       
+       edg_wll_ResetError(ctx);
+
+       edg_wll_IndexedAttrsRequestToXML(ctx, &send_mess);
+       
+       if (set_server_name_and_port(ctx, NULL))
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_query_timeout;
+
+       if (edg_wll_http_send_recv(ctx, "POST /indexedAttrs HTTP/1.1",request_headers, send_mess,
+                       &response,NULL,&message)) 
+               goto err;
+         
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseIndexedAttrs(ctx,message,attrs);
+
+err:
+       free(response);
+       free(message);
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+/* 
+ * wrappers around edg_wll_Query()
+ */
+
+int edg_wll_UserJobs(
+               edg_wll_Context ctx,
+               edg_wlc_JobId **jobsOut,
+               edg_wll_JobStat **statesOut)
+{
+       edg_wll_QueryRec        j[2];
+       
+       memset(j,0,sizeof j);
+       
+       j[0].attr = EDG_WLL_QUERY_ATTR_OWNER;
+       j[0].op = EDG_WLL_QUERY_OP_EQUAL;
+       j[0].value.c = ctx->peerName;
+       
+       return edg_wll_QueryJobs(ctx,j,0,jobsOut,statesOut);
+}
+
+int edg_wll_JobLog(
+       edg_wll_Context ctx,
+       edg_wlc_JobId   job,
+       edg_wll_Event **eventsOut)
+{
+       edg_wll_QueryRec        j[2], e[2];
+
+       memset(j,0,sizeof j);
+       memset(e,0,sizeof e);
+
+       j[0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+       j[0].op = EDG_WLL_QUERY_OP_EQUAL;
+       j[0].value.j = job;
+
+       e[0].attr = EDG_WLL_QUERY_ATTR_LEVEL;
+       e[0].op = EDG_WLL_QUERY_OP_LESS;
+       e[0].value.i = ctx->p_level + 1;
+
+       return edg_wll_QueryEvents(ctx,j,e,eventsOut);
+}
+
+int edg_wll_JobStatus(
+                edg_wll_Context ctx,
+                edg_wlc_JobId job,
+                int flags,
+                edg_wll_JobStat *stat)
+{
+       edg_wll_QueryRec        j[2];
+       edg_wll_JobStat         *statesOut = NULL;
+       int                     ret;
+
+       memset(j,0,sizeof j);
+
+       j[0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+       j[0].op = EDG_WLL_QUERY_OP_EQUAL;
+       j[0].value.j = job;
+       j[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+       ret = edg_wll_QueryJobs(ctx,j,flags,NULL,&statesOut);
+
+       if (ret) return ret;
+
+       if (statesOut) {
+               if (statesOut[0].state == EDG_WLL_JOB_UNDEF) {
+                       memcpy(stat, statesOut, sizeof(edg_wll_JobStat));
+                       free(statesOut);        
+                       ret = edg_wll_SetError(ctx , (edg_wll_ErrorCode) ENOENT, "Query returned no result.");
+               }
+               else {
+                       /* check wheter there is only one field in status reply */
+                       assert(statesOut[1].state == EDG_WLL_JOB_UNDEF);
+                       /* copy only 1st status */
+                       memcpy(stat, statesOut, sizeof(edg_wll_JobStat));
+                       /* release only array of states, keep all links unfreed for the previous copy */
+                       free(statesOut);
+               }
+       }
+
+       return ret;
+}
+
+
+       
+int edg_wll_QueryListener(edg_wll_Context ctx, edg_wlc_JobId job, const char *name, char** host, uint16_t *port) {
+
+       int             i;
+       edg_wll_Event           *events = NULL;
+       int             errCode = 0;
+        edg_wll_QueryRec    jr[2],er[2];
+       int             found = 0;
+
+       memset(jr,0,sizeof jr);
+       memset(er,0,sizeof er);
+        jr[0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+        jr[0].op = EDG_WLL_QUERY_OP_EQUAL;
+        jr[0].value.j = job;
+
+        er[0].attr = EDG_WLL_QUERY_ATTR_EVENT_TYPE;
+        er[0].op = EDG_WLL_QUERY_OP_EQUAL;
+        er[0].value.i = EDG_WLL_EVENT_LISTENER;
+
+        if (edg_wll_QueryEvents(ctx, jr, er, &events)) {
+               return edg_wll_Error(ctx, NULL, NULL);
+       }               
+       
+       for (i=0; events[i].type != EDG_WLL_EVENT_UNDEF; i++) {
+               if (!strcmp(name, events[i].listener.svc_name)) {
+                       found = 1;
+                       if (host != NULL)
+                               *host = strdup(events[i].listener.svc_host);
+                       if (port != NULL)
+                               *port = events[i].listener.svc_port;
+               }
+               edg_wll_FreeEvent(&events[i]);
+       }
+       free(events);
+       
+       if (!found)
+               errCode = ENOENT;
+
+       return edg_wll_SetError(ctx, errCode, NULL);
+}
+
+
+
+int set_server_name_and_port(edg_wll_Context ctx, const edg_wll_QueryRec **job_conditions)
+{
+       int                             i = 0, j,
+                                       found = 0,
+                                       error = 0;
+       int                             srvPort = 0,
+                                       srvPortTmp;
+       char               *srvName = NULL,
+                                  *srvNameTmp;
+
+
+       if ( job_conditions ) for ( j = 0; job_conditions[j]; j++ )
+               for ( i = 0; (job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                       if ( job_conditions[j][i].attr == EDG_WLL_QUERY_ATTR_JOBID)
+                       {
+                               edg_wlc_JobIdGetServerParts(job_conditions[j][i].value.j,&srvNameTmp,&srvPortTmp);
+                               if ( found )
+                               {
+                                       if ( strcmp(srvName, srvNameTmp) || (srvPort != srvPortTmp) )
+                                       {
+                                               free(srvNameTmp); free(srvName);
+                                               return edg_wll_SetError(ctx, EINVAL, "Two different servers specifieed in one query");
+                                       }
+                                       free(srvNameTmp);
+                               }
+                               else
+                               {
+                                       srvName = srvNameTmp;
+                                       srvPort = srvPortTmp;
+                                       found = 1;
+                               }
+                       }       
+
+       if ( found && !ctx->p_query_server_override)
+       { 
+               if (!ctx->srvName)
+               {
+                       ctx->srvName = strdup(srvName);
+                       ctx->srvPort = srvPort;
+                       free(srvName);
+               }
+               else if (strcmp(srvName, ctx->srvName) || (srvPort != ctx->srvPort))
+               {
+                       free(ctx->srvName);
+                       ctx->srvName = strdup(srvName);
+                       ctx->srvPort = srvPort;
+                       free(srvName);
+               }
+       }
+       else if ( !ctx->srvName || !ctx->srvPort )
+       {
+               if (!ctx->p_query_server) 
+                       return(edg_wll_SetError(ctx, (edg_wll_ErrorCode) EINVAL, "Hostname of server to query is not set"));
+               else ctx->srvName = strdup(ctx->p_query_server);
+               if (!ctx->p_query_server_port)
+                       return(edg_wll_SetError(ctx, (edg_wll_ErrorCode) EINVAL, "Port of server to query is not set"));
+               else ctx->srvPort = ctx->p_query_server_port;
+       }
+               
+       return(error);
+}
diff --git a/org.glite.lb.client/src/dump.c b/org.glite.lb.client/src/dump.c
new file mode 100644 (file)
index 0000000..e4e4542
--- /dev/null
@@ -0,0 +1,233 @@
+#ident "$Header$"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <globus_common.h>
+
+#define CLIENT_SBIN_PROG
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/ulm_parse.h"
+#include "glite/lb/xml_parse.h"
+#include "glite/lb/mini_http.h"
+#include "glite/wms/tls/ssl_helpers/ssl_inits.h"
+
+
+#define dprintf(x) { if (debug) printf x; }
+
+static const char rcsid[] = "@(#)$Id$";
+
+static int debug=0;
+
+static void printerr(edg_wll_Context ctx);
+
+static struct option opts[] = {
+       { "from",               required_argument, NULL, 'f'},
+       { "to",                 required_argument, NULL, 't'},
+       { "help",               no_argument, NULL, 'h' },
+       { "version",            no_argument, NULL, 'v' },
+       { "debug",              no_argument, NULL, 'd' },
+       { "server",             required_argument, NULL, 'm' },
+       { NULL,                 no_argument, NULL,  0 }
+};
+
+static void usage(char *me)
+{
+       fprintf(stderr,"usage: %s [option]\n"
+               "       -f, --from YYYYMMDDHHmmss   beginning of the time interval for events to be dumped\n"
+               "       -t, --to YYYYMMDDHHmmss     end of the time interval for events to be dumped\n"
+               "       -h, --help                  display this help\n"
+               "       -v, --version               display version\n"
+               "       -d, --debug                 diagnostic output\n"
+               "       -m, --server                L&B server machine name\n",
+               me);
+}
+
+int main(int argc,char *argv[])
+{
+       edg_wll_DumpRequest *request;
+       edg_wll_DumpResult *result;
+       char *server = NULL;
+       char date[ULM_DATE_STRING_LENGTH+1];
+
+       char *me;
+       int opt;
+       edg_wll_Context ctx;
+
+       /* initialize request to server defaults */
+       request = (edg_wll_DumpRequest *) calloc(1,sizeof(edg_wll_DumpRequest));
+       request->from = EDG_WLL_DUMP_LAST_END;
+       request->to = EDG_WLL_DUMP_NOW;
+
+       /* initialize result */
+       result = (edg_wll_DumpResult *) calloc(1,sizeof(edg_wll_DumpResult));
+
+       me = strrchr(argv[0],'/');
+       if (me) me++; else me=argv[0];
+
+       /* get arguments */
+       while ((opt = getopt_long(argc,argv,"f:t:m:dvh",opts,NULL)) != EOF) {
+
+               switch (opt) {
+
+               case 'f': request->from = (time_t) edg_wll_ULMDateToDouble(optarg); break;
+               case 't': request->to = (time_t) edg_wll_ULMDateToDouble(optarg); break;
+               case 'm': server = optarg; break;
+               case 'd': debug = 1; break;
+               case 'v': fprintf(stdout,"%s:\t%s\n",me,rcsid); exit(0);
+               case 'h':
+               case '?': usage(me); return 1;
+               }
+       }
+
+       /* Initialize Globus common module */
+       dprintf(("Initializing Globus common module..."));
+       if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS) {
+               dprintf(("no.\n"));
+               fprintf(stderr,"Unable to initialize Globus common module\n");
+       } else {
+               dprintf(("yes.\n"));
+       }
+
+       edg_wlc_SSLInitialization();
+
+       /* check request */
+       if (debug) {
+               printf("Dump request:\n");
+               if (request->from < 0) {
+                       printf("- from: %ld.\n",request->from);
+               } else {
+                       if (edg_wll_ULMTimevalToDate(request->from,0,date) != 0) {
+                               fprintf(stderr,"Error parsing 'from' argument.\n");
+                               goto main_end;
+                       }
+                       printf("- from: %ld (i.e. %s).\n",request->from,date);
+               }
+               if (request->to < 0) {
+                       printf("- to: %ld.\n",request->to);
+               } else {
+                       if (edg_wll_ULMTimevalToDate(request->to,0,date) != 0) {
+                               fprintf(stderr,"Error parsing 'to' argument.\n");
+                               goto main_end;
+                       }
+                       printf("- to: %ld (i.e. %s).\n",request->to,date);
+               }
+       }
+
+       /* initialize context */
+       edg_wll_InitContext(&ctx);
+       if ( server )
+       {
+               char *p = strchr(server, ':');
+               if ( p )
+               {
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER_PORT, atoi(p+1));
+                       *p = 0;
+               }
+               edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER, server);
+       }
+
+       /* that is the DumpEvents */
+       dprintf(("Running the edg_wll_DumpEvents...\n"));
+       if (edg_wll_DumpEvents(ctx, request, result) != 0) {
+               fprintf(stderr,"Error running the edg_wll_DumpEvents().\n");
+               printerr(ctx);
+               switch ( edg_wll_Error(ctx, NULL, NULL) )
+               {
+               case ENOENT:
+               case EPERM:
+               case EINVAL:
+                       break;
+               default:
+                       goto main_end;
+               }
+       }
+
+       /* examine the result */
+       dprintf(("Examining the result of edg_wll_DumpEvents...\n"));
+       printf("Dump result:\n");
+       if (result->server_file) {
+               printf("- The jobs were dumped to the file '%s' at the server.\n",result->server_file);
+       } else {
+               printf("- The jobs were not dumped.\n");
+       }
+       if (edg_wll_ULMTimevalToDate(result->from,0,date) != 0) {
+               fprintf(stderr,"Error parsing 'from' argument.\n");
+               goto main_end;
+       }
+       printf("- from: %ld (i.e. %s).\n",result->from,date);
+       if (edg_wll_ULMTimevalToDate(result->to,0,date) != 0) {
+               fprintf(stderr,"Error parsing 'to' argument.\n");
+               goto main_end;
+       }
+       printf("- to: %ld (i.e. %s).\n",result->to,date);
+
+main_end:
+       dprintf(("End.\n"));
+       if (request) free(request);
+       if (result) free(result);
+       edg_wll_FreeContext(ctx);
+       return 0;
+}
+
+
+static void printerr(edg_wll_Context ctx) 
+{
+       char    *errt,*errd;
+
+       edg_wll_Error(ctx,&errt,&errd);
+       fprintf(stderr,"%s (%s)\n",errt,errd);
+}
+
+
+static const char* const request_headers[] = {
+       "Cache-Control: no-cache",
+       "Accept: application/x-dglb",
+       "User-Agent: edg_wll_Api/" PROTO_VERSION "/" COMP_PROTO,
+       "Content-Type: application/x-dglb",
+       NULL
+};
+
+int edg_wll_DumpEvents(
+               edg_wll_Context ctx,
+               const edg_wll_DumpRequest *request,
+               edg_wll_DumpResult *result)
+{
+       int     error;
+       char    *send_mess,
+               *response = NULL,
+               *recv_mess = NULL;
+
+       edg_wll_DumpRequestToXML(ctx, request, &send_mess);
+
+       //edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_TIMEOUT, 4000);
+       ctx->p_tmp_timeout.tv_sec = 400;
+
+       if (set_server_name_and_port(ctx, NULL))
+               goto edg_wll_dumpevents_end;
+
+       error = edg_wll_http_send_recv(ctx,
+                       "POST /dumpRequest HTTP/1.1", request_headers, send_mess,
+                       &response, NULL, &recv_mess);
+       if ( error != 0 )
+               goto edg_wll_dumpevents_end;
+
+       if (http_check_status(ctx, response, &recv_mess))
+               goto edg_wll_dumpevents_end;
+
+       edg_wll_ParseDumpResult(ctx, recv_mess, result);
+
+edg_wll_dumpevents_end:
+       if (response) free(response);
+       if (recv_mess) free(recv_mess);
+       if (send_mess) free(send_mess);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.client/src/load.c b/org.glite.lb.client/src/load.c
new file mode 100644 (file)
index 0000000..514def0
--- /dev/null
@@ -0,0 +1,210 @@
+#ident "$Header$"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <globus_common.h>
+
+#define CLIENT_SBIN_PROG
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/load.h"
+#include "glite/lb/ulm_parse.h"
+#include "glite/lb/xml_parse.h"
+#include "glite/lb/mini_http.h"
+#include "glite/wms/tls/ssl_helpers/ssl_inits.h"
+
+#define dprintf(x) { if (debug) printf x; }
+
+static const char rcsid[] = "@(#)$Id$";
+
+static int debug=0;
+
+static void printerr(edg_wll_Context ctx);
+
+static struct option opts[] = {
+       { "file",               required_argument, NULL, 'f'},
+       { "help",               no_argument, NULL, 'h' },
+       { "version",            no_argument, NULL, 'v' },
+       { "debug",              no_argument, NULL, 'd' },
+       { "server",             required_argument, NULL, 'm' },
+       { NULL,                 no_argument, NULL,  0 }
+};
+
+static void usage(char *me)
+{
+       fprintf(stderr,"usage: %s [option]\n"
+               "       -m, --server                L&B server machine name\n"
+               "       -f, --file filename         file with dumped data to be loaded\n"
+               "       -h, --help                  display this help\n"
+               "       -v, --version               display version\n"
+               "       -d, --debug                 diagnostic output\n",
+               me);
+}
+
+int main(int argc,char *argv[])
+{
+       edg_wll_LoadRequest *request;
+       edg_wll_LoadResult *result;
+       char *server = NULL;
+       char date[ULM_DATE_STRING_LENGTH+1];
+
+       char *me;
+       int opt;
+       edg_wll_Context ctx;
+
+       /* initialize request to server defaults */
+       request = (edg_wll_LoadRequest *) calloc(1,sizeof(edg_wll_LoadRequest));
+       request->server_file = NULL;
+
+       /* initialize result */
+       result = (edg_wll_LoadResult *) calloc(1,sizeof(edg_wll_LoadResult));
+
+       me = strrchr(argv[0],'/');
+       if (me) me++; else me=argv[0];
+
+       /* get arguments */
+       while ((opt = getopt_long(argc,argv,"f:t:m:dvh",opts,NULL)) != EOF) {
+
+               switch (opt) {
+
+               case 'f': request->server_file = optarg; break;
+               case 'm': server = optarg; break;
+               case 'd': debug = 1; break;
+               case 'v': fprintf(stdout,"%s:\t%s\n",me,rcsid); exit(0);
+               case 'h':
+               case '?': usage(me); return 1;
+               }
+       }
+
+       /* Initialize Globus common module */
+       dprintf(("Initializing Globus common module..."));
+       if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS) {
+               dprintf(("no.\n"));
+               fprintf(stderr,"Unable to initialize Globus common module\n");
+       } else {
+               dprintf(("yes.\n"));
+       }
+
+       edg_wlc_SSLInitialization();
+
+       /* initialize context */
+       edg_wll_InitContext(&ctx);
+       if ( server )
+       {
+               char *p = strchr(server, ':');
+               if ( p )
+               {
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER_PORT, atoi(p+1));
+                       *p = 0;
+               }
+               edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER, server);
+       }
+
+       /* check request */
+       if (debug) {
+               printf("Load request:\n");
+               if (request->server_file) {
+                       printf("- server_file: %s.\n",request->server_file);
+               } else {
+                       printf("- server_file: not specified.\n");
+               }
+       }
+
+       /* that is the LoadEvents */
+       dprintf(("Running the edg_wll_LoadEvents...\n"));
+       if (edg_wll_LoadEvents(ctx, request, result) != 0) {
+               fprintf(stderr,"Error running the edg_wll_LoadEvents().\n");
+               printerr(ctx);
+               if ( !result->server_file )
+                       goto main_end;
+       }
+
+       /* examine the result */
+       dprintf(("Examining the result of edg_wll_LoadEvents...\n"));
+       printf("Load result:\n");
+       if (result->server_file)
+               printf("- Unloaded events were stored into the server file '%s'.\n", result->server_file);
+       if (edg_wll_ULMTimevalToDate(result->from,0,date) != 0) {
+               fprintf(stderr,"Error parsing 'from' argument.\n");
+               goto main_end;
+       }
+       printf("- from: %ld (i.e. %s).\n",result->from,date);
+       if (edg_wll_ULMTimevalToDate(result->to,0,date) != 0) {
+               fprintf(stderr,"Error parsing 'to' argument.\n");
+               goto main_end;
+       }
+       printf("- to: %ld (i.e. %s).\n",result->to,date);
+
+main_end:
+       dprintf(("End.\n"));
+       if (request) free(request);
+       if (result)
+       {
+               if (result->server_file)
+                       free(result->server_file);
+               free(result);
+       }
+       edg_wll_FreeContext(ctx);
+       return 0;
+}
+
+
+static void printerr(edg_wll_Context ctx) 
+{
+       char    *errt,*errd;
+
+       edg_wll_Error(ctx,&errt,&errd);
+       fprintf(stderr,"%s (%s)\n",errt,errd);
+}
+
+
+static const char* const request_headers[] = {
+       "Cache-Control: no-cache",
+       "Accept: application/x-dglb",
+       "User-Agent: edg_wll_Api/" PROTO_VERSION "/" COMP_PROTO,
+       "Content-Type: application/x-dglb",
+       NULL
+};
+
+int edg_wll_LoadEvents(
+               edg_wll_Context ctx,
+               const edg_wll_LoadRequest *request,
+               edg_wll_LoadResult *result)
+{
+       int     error;
+       char    *send_mess,
+               *response = NULL,
+               *recv_mess = NULL;
+
+       edg_wll_LoadRequestToXML(ctx, request, &send_mess);
+
+       //edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_TIMEOUT, 4000);
+       ctx->p_tmp_timeout.tv_sec = 400;
+
+       if (set_server_name_and_port(ctx, NULL))
+               goto edg_wll_loadevents_end;
+
+       error = edg_wll_http_send_recv(ctx,
+                       "POST /loadRequest HTTP/1.1", request_headers, send_mess,
+                       &response, NULL, &recv_mess);
+       if ( error != 0 )
+               goto edg_wll_loadevents_end;
+
+       if (http_check_status(ctx, response, &recv_mess))
+               goto edg_wll_loadevents_end;
+
+       edg_wll_ParseLoadResult(ctx, recv_mess, result);
+
+edg_wll_loadevents_end:
+       if (response) free(response);
+       if (recv_mess) free(recv_mess);
+       if (send_mess) free(send_mess);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.client/src/logevent.c.T b/org.glite.lb.client/src/logevent.c.T
new file mode 100644 (file)
index 0000000..faa67c3
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+@@@AUTO
+*/
+
+@@@LANG: C
+
+#ident "$Header$"
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h> // log
+#include <ctype.h> // isspace
+
+#include <globus_common.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/notifid.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/events.h"
+#include "glite/wms/tls/ssl_helpers/ssl_inits.h"
+
+#include "args.h"
+
+    //"    %s -p -l 100000 -j https://localhost/First_JobIV?localhost:7771 -s UserInterface -e jobAbort DG.JOB.ABORT.REASON=\"oops\"\n\n",
+
+// undefine to disable support for -l option
+#define ENABLE_REASON_LENGTH
+
+int main(int argc, char *argv[])
+{
+       char    /* *fmt,*fname = NULL,*/ *fmt_arg=NULL;
+        char* server = NULL,*code = NULL;
+       char*   ff = NULL,*jobid_s = NULL;
+       char    *src_instance = NULL;
+       int     err = 0/*,i,done = 0,fmtlen*/;
+       int     pri = 0;
+        int deb = 0;
+#ifdef ENABLE_REASON_LENGTH
+       int     elength = 0;
+#endif
+       edg_wll_Context ctx;
+        edg_wll_Source src;
+       edg_wll_EventCode event = EDG_WLL_EVENT_UNDEF;
+       edg_wlc_JobId jobid = 0;
+        int (*logev)(edg_wll_Context context,
+                    edg_wll_EventCode event,
+                    char *fmt, ...);
+
+@@@{
+    my %vars = ();
+    for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+              $event->getTypes) {
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+           my $f = selectField $event $_;
+           my $fn = $f->getName;
+           my $type = {
+               "type" => $f->getType,
+               "defval" => $f->getDefaultNullValue
+           };
+
+           $type = { type=>'char *',defval=>'NULL' } if $f->{codes};
+
+           $vars{$fn} = $type;
+       }
+    }
+    foreach $name ( sort keys %vars ) {
+       gen qq{\t$vars{$name}->{"type"} $name = $vars{$name}->{"defval"};\n};
+    }
+@@@}
+
+       edg_wll_Args parray[] = {
+           { EDG_WLL_ARGS_EVENT, "e", "event", "select event type (see -e help)", &event },
+           { EDG_WLL_ARGS_BOOL, "p", "priority", "send as priority event", &pri },
+           { EDG_WLL_ARGS_STRING, "m", "machine", 0, &server },
+           { EDG_WLL_ARGS_SOURCE, "s", "source", "event source (see -s help)", &src },
+           { EDG_WLL_ARGS_STRING, "i", "source-instance", "event source instance", &src_instance },
+           { EDG_WLL_ARGS_STRING, "j", "jobid", "JobId", &jobid_s },
+           { EDG_WLL_ARGS_STRING, "c", "sequence", "event sequence code", &code },
+           { EDG_WLL_ARGS_BOOL, "d", "debug mode", "enable debug mode", &deb },
+#ifdef ENABLE_REASON_LENGTH
+           { EDG_WLL_ARGS_INT, "l", "reason-length", "extend 'reason' string to length (debug only)", &elength, 0, 1000000000 },
+#endif
+@@@{
+    my %typetab = (
+       "char *", "EDG_WLL_ARGS_STRING",
+       "int", "EDG_WLL_ARGS_INT",
+       "edg_wlc_JobId", "EDG_WLL_ARGS_JOBID",
+       "edg_wll_NotifId", "EDG_WLL_ARGS_NOTIFID",
+       "edg_wll_Source", "EDG_WLL_ARGS_SOURCE",
+       "uint16_t", "EDG_WLL_ARGS_UINT16"
+                 );
+    my %vars = ();
+    for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+              $event->getTypes) {
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+           my $f = selectField $event $_;
+            my $ft = $f->{codes} ? 'char *' : $f->getType;
+           my $fn = $f->getName;
+           my $fc = $f->getComment;
+           $namehelp = { "type" => $ft, "help" => $fc };
+#         gen qq{ $fn $ft $fc \n};
+          $vars{$fn} = $namehelp;
+       }
+    }
+    foreach $name ( sort keys %vars ) {
+#      gen qq{ $vars{$name} $name \n };
+       gen qq{\t    \{ $typetab{$vars{$name}->{"type"}}, 0, "$name", "$vars{$name}->{"help"}", &$name \},\n};
+    }
+
+@@@}
+           { EDG_WLL_ARGS_HELP },
+           { EDG_WLL_ARGS_NONE },
+       };
+       edg_wll_ParseArgs(&argc, argv, parray,
+                         "Usage: %s [-d] [-p] [-l eventlen] [-j dg_jobid]"
+                         "[-s source_id] -e event [key=value ...]");
+
+       if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS) {
+               fprintf(stderr, "Cannot initialize Globus common module\n");
+               exit(1);
+       }
+
+       edg_wlc_SSLInitialization();
+
+       edg_wll_InitContext(&ctx);
+
+       /* log the event - priority/normal */
+       logev = (pri) ? edg_wll_LogEventSync : edg_wll_LogEvent;
+
+        /* if no job gived - generate some */
+       if (jobid_s == 0) {
+               const char* s;
+               if (!server) {
+                   s = "localhost";
+                   err = 1; // result fail if used normaly
+               } else
+                   s = server;
+               edg_wlc_JobIdCreate(s, 0, &jobid);
+               jobid_s = edg_wlc_JobIdUnparse(jobid);
+               fprintf(stderr, "JobId not given: created %s (server: %s)\n",jobid_s, s);
+       }
+       else if ((errno = edg_wlc_JobIdParse(jobid_s,&jobid))) {
+               perror(jobid_s);
+               exit(1);
+       }
+
+       if (event == EDG_WLL_EVENT_UNDEF) {
+               fprintf(stderr,"%s: unknown or unspecified event\n",argv[0]);
+               exit(1);
+       }
+       edg_wll_SetParam(ctx, EDG_WLL_PARAM_SOURCE, src);
+       if (src_instance) edg_wll_SetParam(ctx, EDG_WLL_PARAM_INSTANCE, src_instance);
+       edg_wll_SetParam(ctx, EDG_WLL_PARAM_LEVEL,
+                        (deb) ? EDG_WLL_LEVEL_DEBUG : EDG_WLL_LEVEL_SYSTEM);
+
+       if (edg_wll_SetLoggingJob(ctx,jobid,code,EDG_WLL_SEQ_NORMAL)) {
+               char    *et,*ed;
+               edg_wll_Error(ctx,&et,&ed);
+               fprintf(stderr,"SetLoggingJob(%s,%s): %s (%s)\n",jobid_s,code,et,ed);
+               exit(1);
+       }
+
+#ifdef ENABLE_REASON_LENGTH
+       if (elength > 200000000) {
+               fprintf(stderr,"%s: usupported reason message length: %d\n", argv[0], elength);
+       } else if (elength > 0) {
+               int d;
+               int i = 0;
+               reason = realloc(reason, elength + 1);
+                reason[elength] = 0;
+               while (i < (elength)) {
+                        if ((i % 20) == 0 && (i + 14) < elength)
+                               i += sprintf(reason + i, "%d", i);
+                       reason[i++] = '.';
+               }
+               // amount of decimal digits + "end="
+                d = (int)ceil(log(i)/log(10)) + 5;
+               if (i > d)
+                       sprintf(reason + i - (int)(d), "end=%d",i);
+               reason[i] = 0;
+       }
+#endif
+#if 0
+       if (fname) {
+               FILE *f = fopen(fname,"r");
+               long s;
+                size_t r;
+
+               fmt = "%s FILE.CONTENT=\"%|Us\" ";
+
+               if (!f) { perror(fname); return 1; }
+               fseek(f,0L,SEEK_END);
+               s = ftell(f); rewind(f);
+               ff = (char *) malloc(s+1); if (!ff) { perror(NULL); return 0; }
+               r = fread(ff,1,s,f);
+               printf("%s: read %d\n",fname,r);
+               ff[s] = 0;
+               fclose(f);
+       }
+       else
+               fmt = "%s ";
+#endif
+
+       //err = edg_wll_LogEvent(ctx, event, fmt, fmt_arg, ff);
+
+       switch (event)
+       {
+@@@{
+$indent = "\t";
+    for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+              $event->getTypes) {
+
+        my $tu = uc $t;
+       gen $indent."case EDG_WLL_EVENT_$tu :\n";
+
+       selectType $event $t;
+       my $argl = "";
+       for ($event->getFieldsOrdered) {
+           my $f = selectField $event $_;
+           my $fn = $f->getName;
+           my $ft;
+
+#          gen {  $fn  $f->{codes} };
+           if ($f->{codes}) {
+#                        $ft = "enum edg_wll\_$t" . ucfirst $fn;
+               $ft = "char *";
+           } else {
+               $ft = $f->getType;
+           }
+
+           $fn = $f->getType()."ToString($fn)" if $ULMasString{$f->{type}};
+
+           $argl = $argl . ", ";
+           $argl = $argl . $fn;
+       }
+gen qq{\t    //edg_wll_Log$t();
+\t    err |= logev(ctx, EDG_WLL_EVENT_$tu, EDG_WLL_FORMAT_${tu}$argl);
+\t    break;
+};
+    }
+@@@}
+       default:
+           fprintf(stderr, "unknown event\n");
+       }
+
+       edg_wlc_JobIdFree(jobid);
+       if (jobid_s) free(jobid_s); // add all strings
+
+       if (err) {
+           char        *et,*ed;
+
+           edg_wll_Error(ctx,&et,&ed);
+           fprintf(stderr,"%s: edg_wll_LogEvent*(): %s (%s)\n",
+                   argv[0],et,ed);
+           free(et); free(ed);
+       }
+
+       code = edg_wll_GetSequenceCode(ctx);
+       puts(code);
+       free(code);
+
+       edg_wll_FreeContext(ctx);
+
+       if (ff) free(ff);
+       if (fmt_arg) free(fmt_arg);
+
+       return err;
+}
diff --git a/org.glite.lb.client/src/notification.c b/org.glite.lb.client/src/notification.c
new file mode 100644 (file)
index 0000000..6eb186a
--- /dev/null
@@ -0,0 +1,743 @@
+#ident "$Header"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+
+#include "glite/lb/notification.h"
+#include "glite/lb/events.h"
+#include "glite/lb/log_proto.h"
+#include "glite/lb/mini_http.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/xml_parse.h"
+#include "glite/lb/events_parse.h"
+#include "glite/lb/il_string.h"
+#include "glite/lb/escape.h"
+
+#include "connection.h"
+
+#define CON_QUEUE               10     /* listen() queue limit */
+
+/* XXX: moving all request_headers to one file would be nice
+ */
+static const char* const request_headers[] = {
+       "Cache-Control: no-cache",
+       "Accept: application/x-dglb",
+       "User-Agent: edg_wll_Api/" PROTO_VERSION "/" COMP_PROTO,
+       "Content-Type: application/x-dglb",
+       NULL
+};
+
+
+
+
+/* Decrement timeout
+ */
+static int decrement_timeout(struct timeval *timeout, struct timeval before, struct timeval after)
+{
+       if (!timeout) 
+               return(0);      // wait indefinitely
+       
+        (*timeout).tv_sec = (*timeout).tv_sec - (after.tv_sec - before.tv_sec);
+        (*timeout).tv_usec = (*timeout).tv_usec - (after.tv_usec - before.tv_usec);
+        while ( (*timeout).tv_usec < 0) {
+                (*timeout).tv_sec--;
+                (*timeout).tv_usec += 1000000;
+        }
+        if ( ((*timeout).tv_sec < 0) || (((*timeout).tv_sec == 0) && ((*timeout).tv_usec == 0)) ) return(1);
+        else return(0);
+}
+
+
+
+/* Split address to name & port
+ */
+static void get_name_and_port(const char *address, char **name, int *port)
+{
+       char *n = NULL, *p;
+       
+       n = strdup(address);
+       p = strchr(n, ':');
+       if (p)
+       {
+               *port = atoi(p+1);
+               *p = 0;
+       }
+       *name = strdup(n);
+       free(n);
+}      
+
+
+static int my_bind(edg_wll_Context ctx, const char *name, int port, int *fd)
+{
+       struct  sockaddr_in     a;
+       socklen_t               alen = sizeof(a);
+       int                     sock;
+               
+       sock = socket(PF_INET,SOCK_STREAM,0);
+       if (sock<0) 
+               return  edg_wll_SetError(ctx, errno, "socket() failed");
+
+       a.sin_family = AF_INET;
+       a.sin_port = htons(port);
+       a.sin_addr.s_addr = name? inet_addr(name): htonl(INADDR_ANY);
+
+//     setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+       if (bind(sock,(struct sockaddr *)&a,alen))
+               return  edg_wll_SetError(ctx, errno, "bind() failed");
+
+       
+       if (listen(sock,CON_QUEUE)) 
+               return edg_wll_SetError(ctx, errno, "listen() failed");
+       
+       *fd = sock;
+       
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+       
+
+
+static int set_server_name_and_port(edg_wll_Context ctx) 
+{
+
+       if (!ctx->p_notif_server)
+               return(edg_wll_SetError(ctx, (edg_wll_ErrorCode) EINVAL, 
+                               "Hostname of server to notif is not set"));
+       else {
+               free(ctx->srvName);
+               ctx->srvName = strdup(ctx->p_notif_server);
+       }
+       if (!ctx->p_notif_server_port)
+               return(edg_wll_SetError(ctx, (edg_wll_ErrorCode) EINVAL, 
+                               "Port of server to notif is not set"));
+       else ctx->srvPort = ctx->p_notif_server_port;
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+static int get_client_address(
+               edg_wll_Context ctx, 
+               int fd,
+               const char *address_override,
+               char **address) 
+       
+{
+       struct  sockaddr_in     a;
+       socklen_t               alen = sizeof(a);
+       struct hostent          *he;
+       char                    *name = NULL;
+       int                     port = 0;
+       
+       
+       if (address_override) {
+               struct in_addr in;
+               
+               get_name_and_port(address_override, &name, &port);
+               
+               if ( (he = gethostbyname((const char *) name)) == NULL) {
+                       edg_wll_SetError(ctx, errno, "gethostbyname() failed");
+                       goto err;
+               }
+               
+               free(name);
+               
+               memmove(&in.s_addr, he->h_addr_list[0], sizeof(in.s_addr));
+               name = strdup(inet_ntoa(in));
+       
+               if ( (he = gethostbyname((const char *) name)) == NULL) {
+                       edg_wll_SetError(ctx, errno, "gethostbyname() failed");
+                       goto err;
+               }
+       
+               /* Test whether open sockket represents the same address as address_override
+                * if not, close ctx->notifSock and bind to new socket corresponding to 
+                * address_override
+                */
+               if (ctx->notifSock >= 0) {
+                       if (getsockname(ctx->notifSock, &a, &alen)) 
+                               return edg_wll_SetError(ctx, errno, "getsockname() failed");
+                       
+                       if ( (strcmp(inet_ntoa(a.sin_addr), name)) || (ntohs(a.sin_port) != port) ) {
+                               
+                               if (close(ctx->notifSock)) {
+                                       edg_wll_SetError(ctx, errno, "close() failed");
+                                       goto err;
+                               }
+                               ctx->notifSock = -1;
+                               
+                               if (my_bind(ctx, name, port, &(ctx->notifSock))) 
+                                       goto err;
+                       }
+               }
+               else {  // create new socket 
+                       if (my_bind(ctx, name, port, &(ctx->notifSock))) 
+                               goto err;
+               }
+               
+               *address = strdup(address_override);
+       }
+       else {  // address_override == NULL
+               
+               if (fd == -1) {
+                       if (ctx->notifSock == -1)       
+                               // create new socket
+                               if (my_bind(ctx, NULL, 0, &(ctx->notifSock)))
+                                               goto err;
+                       // else: resue socket
+               }
+               else    
+                       // used supplied socket
+                       ctx->notifSock = fd;
+       
+               if (getsockname(ctx->notifSock, &a, &alen)) 
+                       return edg_wll_SetError(ctx, errno, "getsockname() failed");
+
+               if (a.sin_addr.s_addr == INADDR_ANY)
+                       asprintf(address,"0.0.0.0:%d", ntohs(a.sin_port));
+               else
+                       asprintf(address,"%s:%d", inet_ntoa(a.sin_addr), ntohs(a.sin_port));
+       }
+
+err:
+       free(name);
+       
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_NotifNew(
+        edg_wll_Context         ctx,
+        edg_wll_QueryRec        const * const *conditions,
+        int                     fd,
+        const char              *address_override,
+        edg_wll_NotifId         *id_out,
+        time_t                  *valid) 
+{
+       edg_wll_NotifId         notifId = NULL;
+       char                    *address = NULL, *send_mess = NULL,
+                               *recv_mess = NULL, *response = NULL;
+       int                     ret;    
+       
+
+       edg_wll_ResetError(ctx);
+
+       if ( (ret = set_server_name_and_port(ctx)) ) 
+               goto err;
+               
+       if ( (ret = edg_wll_NotifIdCreate(ctx->srvName,ctx->srvPort,&notifId)) )
+               goto err;
+
+       if ( (ret = get_client_address(ctx, fd, address_override, &address)) )
+               goto err;
+       
+       if ( (ret = edg_wll_NotifRequestToXML(ctx, "New", notifId, address, 
+                       EDG_WLL_NOTIF_NOOP, conditions, &send_mess)) )
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_notif_timeout;
+       
+       if ( (ret = edg_wll_http_send_recv(ctx, "POST /notifRequest HTTP/1.1",
+                       request_headers,send_mess,
+                       &response,NULL,&recv_mess)) )
+               goto err;
+       
+       if ( (ret = http_check_status(ctx,response)) )
+               goto err;
+
+       ret = edg_wll_ParseNotifResult(ctx, recv_mess, valid);
+
+
+err:
+       if (ret != 0) {
+               if (notifId) edg_wll_NotifIdFree(notifId);
+               *id_out = NULL;
+               *valid = -1;
+       }
+       else
+               *id_out = notifId;
+               
+       free(address);
+       free(recv_mess);
+       free(send_mess);
+       free(response);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_NotifBind(
+        edg_wll_Context         ctx,
+        const edg_wll_NotifId   id,
+        int                     fd,
+        const char              *address_override,
+        time_t                  *valid)
+{
+       char    *address = NULL, *send_mess = NULL,
+                       *recv_mess = NULL, *response = NULL;
+       
+       
+       edg_wll_ResetError(ctx);
+
+       
+       // if a local listening socket active, close it
+       if (ctx->notifSock >= 0) {
+               if (close(ctx->notifSock)) 
+                       return edg_wll_SetError(ctx, errno, "close() failed");
+               else
+                       ctx->notifSock = -1;
+       }
+
+       if (set_server_name_and_port(ctx)) 
+               goto err;
+               
+       if (get_client_address(ctx, fd, address_override, &address))
+               goto err;
+
+       if (edg_wll_NotifRequestToXML(ctx, "Bind", id, address, 
+                       EDG_WLL_NOTIF_NOOP, NULL, &send_mess)) 
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_notif_timeout;
+       
+       if (edg_wll_http_send_recv(ctx, "POST /notifRequest HTTP/1.1",
+                       request_headers,send_mess,
+                       &response,NULL,&recv_mess)) 
+               goto err;
+       
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseNotifResult(ctx, recv_mess, valid);
+
+err:
+       free(address);
+       free(recv_mess);
+       free(send_mess);
+       free(response);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_NotifChange(
+        edg_wll_Context         ctx,
+        const edg_wll_NotifId   id,
+        edg_wll_QueryRec        const * const * conditions,
+        edg_wll_NotifChangeOp   op)
+{
+       char    *send_mess = NULL, *recv_mess = NULL, *response = NULL;
+       time_t  not_used;
+       
+       
+       edg_wll_ResetError(ctx);
+
+       
+       if (set_server_name_and_port(ctx)) 
+               goto err;
+               
+       if (edg_wll_NotifRequestToXML(ctx, "Change", id, NULL, 
+                       op, conditions, &send_mess)) 
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_notif_timeout;
+       
+       if (edg_wll_http_send_recv(ctx, "POST /notifRequest HTTP/1.1",
+                       request_headers,send_mess,
+                       &response,NULL,&recv_mess)) 
+               goto err;
+       
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseNotifResult(ctx, recv_mess, &not_used);
+
+err:
+       free(recv_mess);
+       free(send_mess);
+       free(response);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_NotifRefresh(
+        edg_wll_Context         ctx,
+        const edg_wll_NotifId   id,
+        time_t                  *valid)
+{
+       char    *send_mess = NULL, *recv_mess = NULL, *response = NULL;
+       
+       
+       edg_wll_ResetError(ctx);
+
+       
+       if (set_server_name_and_port(ctx)) 
+               goto err;
+               
+       if (edg_wll_NotifRequestToXML(ctx, "Refresh", id, NULL, 
+                       EDG_WLL_NOTIF_NOOP, NULL, &send_mess)) 
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_notif_timeout;
+       
+       if (edg_wll_http_send_recv(ctx, "POST /notifRequest HTTP/1.1",
+                       request_headers,send_mess,
+                       &response,NULL,&recv_mess)) 
+               goto err;
+       
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseNotifResult(ctx, recv_mess, valid);
+
+err:
+       free(recv_mess);
+       free(send_mess);
+       free(response);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_NotifDrop(
+        edg_wll_Context         ctx,
+        edg_wll_NotifId         *id)
+{
+       char    *send_mess = NULL, *recv_mess = NULL, *response = NULL;
+       time_t  not_used;
+       
+       
+       edg_wll_ResetError(ctx);
+
+       
+       if (set_server_name_and_port(ctx)) 
+               goto err;
+               
+       if (edg_wll_NotifRequestToXML(ctx, "Drop", id, NULL, 
+                       EDG_WLL_NOTIF_NOOP, NULL, &send_mess)) 
+               goto err;
+
+       ctx->p_tmp_timeout = ctx->p_notif_timeout;
+       
+       if (edg_wll_http_send_recv(ctx, "POST /notifRequest HTTP/1.1",
+                       request_headers,send_mess,
+                       &response,NULL,&recv_mess)) 
+               goto err;
+       
+       if (http_check_status(ctx,response))
+               goto err;
+
+       edg_wll_ParseNotifResult(ctx, recv_mess, &not_used);
+
+err:
+       free(recv_mess);
+       free(send_mess);
+       free(response);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+static int recv_notif(edg_wll_Context ctx)
+{
+       int     ret, len;
+       char    fbuf[17];
+       size_t  total;
+
+       
+       if (ctx->connPool[ctx->connToUse].buf) {
+               free(ctx->connPool[ctx->connToUse].buf);
+               ctx->connPool[ctx->connToUse].buf = NULL;
+       }
+       ctx->connPool[ctx->connToUse].bufUse = 0;
+       ctx->connPool[ctx->connToUse].bufSize = 0;
+
+       
+       if ((ret=edg_wll_ssl_read_full(ctx->connPool[ctx->connToUse].ssl,
+                                       fbuf,17,&ctx->p_tmp_timeout,&total)) < 0) 
+               switch (ret) {
+                       case EDG_WLL_SSL_ERROR_TIMEOUT: 
+                               return edg_wll_SetError(ctx,ETIMEDOUT,"read message header");
+                       case EDG_WLL_SSL_ERROR_EOF: 
+                               return edg_wll_SetError(ctx,ENOTCONN,NULL);
+                       default: 
+                               return edg_wll_SetError(ctx,EDG_WLL_ERROR_SSL,"read message header");
+       }
+
+       if ((len = atoi(fbuf)) <= 0) {
+               return edg_wll_SetError(ctx,EINVAL,"message length");
+       }
+
+       ctx->connPool[ctx->connToUse].bufSize = len+1;
+
+       ctx->connPool[ctx->connToUse].buf = (char *) malloc(
+               ctx->connPool[ctx->connToUse].bufSize);
+       
+       if (!ctx->connPool[ctx->connToUse].buf) {
+               return edg_wll_SetError(ctx, ENOMEM, "recv_notif()");
+       }
+       
+
+       if ((ret=edg_wll_ssl_read_full(ctx->connPool[ctx->connToUse].ssl,
+                                       ctx->connPool[ctx->connToUse].buf,
+                                       len,
+                                       &ctx->p_tmp_timeout,&total)) < 0) {
+               free(ctx->connPool[ctx->connToUse].buf);
+               ctx->connPool[ctx->connToUse].bufUse = 0;
+               ctx->connPool[ctx->connToUse].bufSize = 0;
+               return edg_wll_SetError(ctx,
+                       ret == EDG_WLL_SSL_ERROR_TIMEOUT ?
+                               ETIMEDOUT : EDG_WLL_ERROR_SSL,
+                       "read message");
+       }
+
+
+       ctx->connPool[ctx->connToUse].buf[len] = 0;
+       ctx->connPool[ctx->connToUse].bufUse = len+1;
+
+       
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+static int send_reply(const edg_wll_Context ctx)
+{
+       int     ret, len, err_code, err_code_min = 0, max_len = 256;
+       char    *p, *err_msg = NULL, buf[max_len];
+       size_t  total;
+
+       
+       err_code = edg_wll_Error(ctx,NULL,&err_msg);
+
+       if (!err_msg) err_msg=strdup("OK");
+       
+       len = 17 + len_int(err_code) + len_int(err_code_min) +len_string(err_msg);
+       if(len > max_len) {
+               edg_wll_SetError(ctx,E2BIG,"create_reply()");
+               goto err;
+       }
+
+       snprintf(buf, max_len, "%16d\n", len - 17);
+       p = buf + 17;
+       p = put_int(p, err_code);
+       p = put_int(p, err_code_min);
+       p = put_string(p, err_msg);
+
+       
+       if ((ret = edg_wll_ssl_write_full(ctx->connPool[ctx->connToUse].ssl,
+                                       buf,len,&ctx->p_tmp_timeout,&total)) < 0) {
+               edg_wll_SetError(ctx,
+                               ret == EDG_WLL_SSL_ERROR_TIMEOUT ? 
+                                       ETIMEDOUT : EDG_WLL_ERROR_SSL,
+                                       "write reply");
+               goto err;
+       }
+       
+err:
+       free(err_msg);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+int edg_wll_NotifReceive(
+        edg_wll_Context         ctx,
+        int                     fd,
+        const struct timeval    *timeout,
+        edg_wll_JobStat         *state_out,
+        edg_wll_NotifId         *id_out)
+{
+       fd_set                  fds;    
+       struct sockaddr_in      a;
+       int                     recv_sock, alen;
+       edg_wll_Event           *event = NULL;
+       struct timeval          start_time,check_time,tv;
+       char                    *p = NULL, *ucs = NULL,
+                                       *event_char = NULL, *jobstat_char = NULL;
+       
+       
+
+/* start timer */
+       gettimeofday(&start_time,0);
+       
+       if (fd == -1) {
+               if (ctx->notifSock == -1) {
+                       edg_wll_SetError(ctx, EINVAL, "No client socket opened.");
+                       goto err;
+               }
+               else {
+                       fd = ctx->notifSock;
+               }
+       }
+
+       FD_ZERO(&fds);
+       FD_SET(fd,&fds);
+       
+       tv.tv_sec = timeout->tv_sec;
+       tv.tv_usec = timeout->tv_usec;
+       
+       switch(select(fd+1, &fds, NULL, NULL, &tv)) {
+               case -1:
+                       edg_wll_SetError(ctx, errno, "select() failed");
+                       goto err;
+               case 0:
+                       edg_wll_SetError(ctx, ETIMEDOUT, "select() timeouted");
+                       goto err;
+               default:
+                       break;
+       }
+
+/* check time */
+       gettimeofday(&check_time,0);
+       if (decrement_timeout(&tv, start_time, check_time)) {
+               edg_wll_SetError(ctx, ETIMEDOUT, "edg_wll_NotifReceive()");
+               goto err;
+       }
+       
+       start_time = check_time;
+       
+       alen=sizeof(a);
+       recv_sock = accept(fd,&a,&alen);
+       if (recv_sock <0) {
+               edg_wll_SetError(ctx, errno, "accept() failed");
+               goto err;
+       }
+
+
+       ctx->connPool[ctx->connToUse].ssl =
+               edg_wll_ssl_accept(ctx->connPool[ctx->connToUse].gsiCred,recv_sock,&tv);
+       
+       if (ctx->connPool[ctx->connToUse].ssl == NULL) {
+               edg_wll_SetError(ctx, errno, "SSL hanshake failed.");
+               goto err;       
+       }
+       
+/* check time */
+       gettimeofday(&check_time,0);
+       if (decrement_timeout(&tv, start_time, check_time)) {
+               edg_wll_SetError(ctx, ETIMEDOUT, "edg_wll_NotifReceive()");
+               goto err;
+       }
+       
+       start_time = check_time;
+       
+
+       ctx->p_tmp_timeout = tv;
+               
+       if (recv_notif(ctx)) {
+               /* error set in recv_notif() */
+               goto err;
+       }       
+
+       if (send_reply(ctx)) {
+               /* error set in send_reply() */
+               goto err;
+       }       
+       
+       p = ctx->connPool[ctx->connToUse].buf;
+       p = get_string(p, &ucs);
+       if (p == NULL) return edg_wll_SetError(ctx,EDG_WLL_IL_PROTO,"reading UCS");
+       free(ucs);
+
+       p = get_string(p, &event_char);
+       if (p == NULL) {
+               free(ucs);
+               return edg_wll_SetError(ctx,EDG_WLL_IL_PROTO,"reading event string");;
+       }
+       
+/* check time */
+       gettimeofday(&check_time,0);
+       if (decrement_timeout(&tv, start_time, check_time)) {
+               edg_wll_SetError(ctx, ETIMEDOUT, "edg_wll_NotifReceive()");
+               goto err;
+       }
+       
+       start_time = check_time;
+               
+       event = edg_wll_InitEvent(EDG_WLL_EVENT_NOTIFICATION);
+       if (edg_wll_ParseNotifEvent(ctx, event_char, &event)) {
+               goto err;
+       }
+
+       jobstat_char = edg_wll_UnescapeXML((const char *) event->notification.jobstat);
+       if (jobstat_char == NULL) {
+               edg_wll_SetError(ctx, EINVAL, "edg_wll_UnescapeXML()");
+               goto err;
+       }
+               
+       /* fill in return values
+        */
+       if ( edg_wll_ParseJobStat(ctx, jobstat_char, 
+                               strlen(jobstat_char), state_out)) {
+               goto err;
+       }
+       
+       *id_out = event->notification.notifId;
+       event->notification.notifId = NULL;
+       
+       
+err:
+       if (event) { 
+               edg_wll_FreeEvent(event);
+               // XXX - konzultovat s honikem; podle meho by to free 
+               // mel delat uz edg_wll_FreeEvent
+               //free(event);
+       }
+
+       free(ctx->connPool[ctx->connToUse].buf);
+       ctx->connPool[ctx->connToUse].buf = NULL;
+       ctx->connPool[ctx->connToUse].bufUse = 0;
+       ctx->connPool[ctx->connToUse].bufSize = 0;
+       
+       free(event_char);
+       free(jobstat_char);
+
+       // XXX
+       // konzultovat s Danem
+       edg_wll_ssl_close(ctx->connPool[ctx->connToUse].ssl);
+       ctx->connPool[ctx->connToUse].ssl = NULL;
+       
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+int edg_wll_NotifGetFd(
+        edg_wll_Context         ctx)
+{
+       if (ctx->notifSock == -1) {
+               edg_wll_SetError(ctx, EBADF, "Client socket is not opened.");
+               return -1;
+       }
+       
+       return ctx->notifSock;
+}
+
+
+int edg_wll_NotifCloseFd(
+        edg_wll_Context         ctx)
+{
+       int err;
+       
+       if (ctx->notifSock >= 0) {
+               if (ctx->connPool[ctx->connToUse].ssl) {
+                       edg_wll_ssl_close(ctx->connPool[ctx->connToUse].ssl);
+                       ctx->connPool[ctx->connToUse].ssl = NULL;
+               }
+               err = close(ctx->notifSock);
+               ctx->notifSock = -1;
+               
+               if (err) 
+                       return edg_wll_SetError(ctx, errno, "close() failed");
+       }
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.client/src/prod_proto.c b/org.glite.lb.client/src/prod_proto.c
new file mode 100644 (file)
index 0000000..696d58d
--- /dev/null
@@ -0,0 +1,152 @@
+#ident "$Header$"
+
+#include "prod_proto.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/escape.h"
+
+#include <signal.h>
+#include <string.h>
+#include <sys/types.h>
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_log_proto_client - handle outgoing data
+ *
+ * Returns: 0 if done properly or errno
+ *
+ * Calls:
+ *
+ * Algorithm:
+ *
+ *----------------------------------------------------------------------
+ */
+int edg_wll_log_proto_client(edg_wll_Context context, SSL *ssl, edg_wll_LogLine logline/*, int priority,*/)
+{
+       char    header[EDG_WLL_LOG_SOCKET_HEADER_LENGTH+1];
+       int     err;
+       int     answer;
+       u_int8_t answer_end[4];
+       int     count;
+       int     size;
+       u_int8_t size_end[4];
+
+       errno = err = answer = count = 0;
+       size = strlen(logline)+1;
+       size_end[0] = size & 0xff; size >>= 8;
+       size_end[1] = size & 0xff; size >>= 8;
+       size_end[2] = size & 0xff; size >>= 8;
+       size_end[3] = size;
+       size = strlen(logline)+1;
+       edg_wll_ResetError(context);
+
+       /* send header */
+#ifdef EDG_WLL_LOG_STUB
+       fprintf(stderr,"Sending socket header...\n");
+#endif
+       sprintf(header,"%s",EDG_WLL_LOG_SOCKET_HEADER);
+       header[EDG_WLL_LOG_SOCKET_HEADER_LENGTH]='\0';
+       if ((err = edg_wll_ssl_write_full(ssl, header, EDG_WLL_LOG_SOCKET_HEADER_LENGTH, &context->p_tmp_timeout, &count)) < 0) {
+               answer = edg_wll_log_proto_client_failure(context,err,"send header");
+               goto edg_wll_log_proto_client_end;
+       }
+
+/* XXX: obsolete
+#ifdef EDG_WLL_LOG_STUB
+       fprintf(stderr,"Sending message priority...\n");
+#endif
+       count = 0;
+        if ((err = edg_wll_ssl_write_full(ssl, &priority, sizeof(priority), &context->p_tmp_timeout, &count)) < 0) {
+                answer = edg_wll_log_proto_client_failure(context,err,"send message priority");
+                goto edg_wll_log_proto_client_end;
+        }
+*/
+
+#ifdef EDG_WLL_LOG_STUB
+       fprintf(stderr,"Sending message size...\n");
+#endif
+       count = 0;
+       if ((err = edg_wll_ssl_write_full(ssl, size_end, 4, &context->p_tmp_timeout, &count)) < 0) {
+                answer = edg_wll_log_proto_client_failure(context,err,"send message size");
+                goto edg_wll_log_proto_client_end;
+        }
+
+       /* send message */
+#ifdef EDG_WLL_LOG_STUB
+       fprintf(stderr,"Sending message to socket...\n");
+#endif
+       count = 0;
+       if (( err = edg_wll_ssl_write_full(ssl, logline, size, &context->p_tmp_timeout, &count)) < 0) {
+               answer = edg_wll_log_proto_client_failure(context,err,"send message");
+               goto edg_wll_log_proto_client_end;
+       }
+
+       /* get answer */
+#ifdef EDG_WLL_LOG_STUB
+       fprintf(stderr,"Reading answer from server...\n");
+#endif
+       count = 0;
+       if ((err = edg_wll_ssl_read_full(ssl, answer_end, 4, &context->p_tmp_timeout, &count)) < 0 ) {
+               answer = edg_wll_log_proto_client_failure(context,err,"get answer");
+/* FIXME: update the answer (in context?) to EAGAIN or not?
+               answer = EAGAIN;
+*/
+       } else {
+               answer = answer_end[3]; answer <<=8;
+               answer |= answer_end[2]; answer <<=8;
+               answer |= answer_end[1]; answer <<=8;
+               answer |= answer_end[0];
+#ifdef EDG_WLL_LOG_STUB
+               fprintf(stderr,"Read answer \"%d\"\n",answer);
+#endif
+               edg_wll_SetError(context,answer,"answer read from locallogger");
+       }
+
+edg_wll_log_proto_client_end:
+
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_log_proto_client_failure - handle protocol failures on the client side
+ *
+ * Returns: errno
+ *
+ *----------------------------------------------------------------------
+ */
+int edg_wll_log_proto_client_failure(edg_wll_Context context, int code, const char *text)
+{
+       const char      *func="edg_wll_log_proto_client()";
+        static char     err[256];
+        int             ret = 0;
+
+       edg_wll_ResetError(context);
+
+       if(code>0)
+                return(0);
+
+       switch(code) {
+                case EDG_WLL_SSL_ERROR_EOF: 
+                       snprintf(err, sizeof(err), "%s: Error %s, EOF occured;", func, text);   
+                       ret = edg_wll_SetError(context,ENOTCONN,err);
+                       break;
+                case EDG_WLL_SSL_ERROR_TIMEOUT: 
+                       snprintf(err, sizeof(err), "%s: Error %s, timeout expired;", func, text);       
+                       ret = edg_wll_SetError(context,ENOTCONN,err);
+                       break;
+               case EDG_WLL_SSL_ERROR_ERRNO: // XXX: perror("edg_wll_ssl_read()"); break;
+                       snprintf(err, sizeof(err), "%s: Error %s, system error occured;", func, text);  
+                       ret = edg_wll_SetError(context,ENOTCONN,err);
+                       break;
+                case EDG_WLL_SSL_ERROR_SSL:
+                       snprintf(err, sizeof(err), "%s: Error %s, SSL error occured; %s;", func, text,
+                                ERR_reason_error_string(ERR_get_error()));
+                       ret = edg_wll_SetError(context,ENOTCONN,err);
+                        break;
+               default:
+                       break;
+       }
+       return ret;
+}
diff --git a/org.glite.lb.client/src/prod_proto.h b/org.glite.lb.client/src/prod_proto.h
new file mode 100644 (file)
index 0000000..8b22258
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef __EDG_WORKLOAD_LOGGING_CLIENT_PROD_PROTO_H__
+#define __EDG_WORKLOAD_LOGGING_CLIENT_PROD_PROTO_H__
+
+#ident "$Header$"
+
+/**
+ * \file edg/workload/logging/client/prod_proto.h
+ * \brief client (producer) part of the logging protocol
+ * \note private
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#include "glite/lb/log_proto.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/dgssl.h"
+
+int edg_wll_log_proto_client(edg_wll_Context context, SSL *ssl, edg_wll_LogLine logline/*, int priority,*/);
+int edg_wll_log_proto_client_failure(edg_wll_Context context, int code, const char *text);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_CLIENT_PROD_PROTO_H__ */
diff --git a/org.glite.lb.client/src/producer.c b/org.glite.lb.client/src/producer.c
new file mode 100644 (file)
index 0000000..e21b176
--- /dev/null
@@ -0,0 +1,594 @@
+/**
+ * \file producer.c
+ * \author Jan Pospisil
+ */
+
+#ident "$Header$"
+
+#include <globus_gss_assist.h>
+#include <globus_config.h>
+
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+#include "glite/wms/jobid/strmd5.h"
+#include "glite/lb/consumer.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/ulm_parse.h"
+#include "glite/lb/trio.h"
+
+#include "prod_proto.h"
+
+/**
+ *----------------------------------------------------------------------
+ * Connects to local-logger and sends already formatted ULM string
+ * \brief helper logging function
+ * \param context      INOUT context to work with,
+ * \param priority     IN priority flag (0 for async, 1 for sync)
+ * \param logline      IN formated ULM string
+ *----------------------------------------------------------------------
+ */
+static int edg_wll_DoLogEvent(
+       edg_wll_Context context,
+/*     int priority,  */
+       edg_wll_LogLine logline)
+{
+       int     ret,answer;
+       void    *cred_handle = NULL;
+       SSL     *ssl = NULL;
+#ifdef EDG_WLL_LOG_STUB
+       char    *my_subject_name = NULL;
+#endif
+
+       edg_wll_ResetError(context);
+       ret = answer = 0;
+
+   /* open a SSL connection to the local-logger: */
+
+#ifdef EDG_WLL_LOG_STUB
+       fprintf(stderr,"Logging to host %s, port %d\n",
+                       context->p_destination, context->p_dest_port);
+#endif
+       
+       if ((cred_handle=edg_wll_ssl_init(SSL_VERIFY_PEER, 0,
+                       context->p_proxy_filename ? context->p_proxy_filename : context->p_cert_filename,
+                       context->p_proxy_filename ? context->p_proxy_filename : context->p_key_filename,
+                       0, 0)) == NULL) {
+               edg_wll_SetError(context,EDG_WLL_ERROR_SSL,"edg_wll_ssl_init()"); 
+               goto edg_wll_DoLogEvent_end; 
+       }
+#ifdef EDG_WLL_LOG_STUB
+       edg_wll_ssl_get_my_subject(cred_handle,&my_subject_name);
+       if (my_subject_name != NULL) {
+               fprintf(stderr,"Using certificate: %s\n",my_subject_name);
+               free(my_subject_name);
+       } else {
+               edg_wll_SetError(context,ECONNREFUSERD,"edg_wll_ssl_get_my_subject()");
+               goto edg_wll_DoLogEvent_end;
+       }
+#endif
+       if ((answer = edg_wll_ssl_connect(cred_handle,
+                       context->p_destination, context->p_dest_port, 
+                       &context->p_tmp_timeout, &ssl)) < 0) {
+               switch(answer) {
+                case EDG_WLL_SSL_ERROR_EOF:
+                       edg_wll_SetError(context,ENOTCONN,"edg_wll_ssl_connect()");
+                       break;
+                case EDG_WLL_SSL_ERROR_TIMEOUT:
+                       edg_wll_SetError(context,ETIMEDOUT,"edg_wll_ssl_connect()");
+                       break;
+                case EDG_WLL_SSL_ERROR_ERRNO:
+                       edg_wll_SetError(context,errno,"edg_wll_ssl_connect()");
+                       break;
+                case EDG_WLL_SSL_ERROR_SSL: 
+                       {
+                               const char *msg1;
+                               char *msg2;
+                               msg1 = ERR_reason_error_string(ERR_get_error());
+                               asprintf(&msg2, "edg_wll_ssl_connect(): %s", msg1);
+                               edg_wll_SetError(context,EDG_WLL_ERROR_SSL,msg2);
+                               free(msg2);
+                       }
+                       break;
+               case EDG_WLL_SSL_ERROR_HERRNO:
+                       { 
+                               const char *msg1;
+                               char *msg2;
+                               msg1 = hstrerror(errno);
+                               asprintf(&msg2, "edg_wll_ssl_connect(): %s", msg1);
+                               edg_wll_SetError(context,EDG_WLL_ERROR_DNS, msg2);
+                               free(msg2);
+                       }
+                        break;
+               default:
+                       edg_wll_SetError(context,ECONNREFUSED,"edg_wll_ssl_connect(): unknown");
+                       break;
+               }
+               goto edg_wll_DoLogEvent_end;
+       }
+
+   /* and send the message to the local-logger: */
+
+       answer = edg_wll_log_proto_client(context,ssl,logline/*,priority*/);
+
+       switch(answer) {
+               case 0:
+               case EINVAL:
+               case ENOSPC:
+               case ENOMEM:
+               case EDG_WLL_ERROR_SSL:
+               case EDG_WLL_ERROR_DNS:
+               case ENOTCONN:
+               case ECONNREFUSED:
+               case ETIMEDOUT:
+               case EAGAIN:
+                       break;
+               case EDG_WLL_ERROR_PARSE_EVENT_UNDEF:
+               case EDG_WLL_ERROR_PARSE_MSG_INCOMPLETE:
+               case EDG_WLL_ERROR_PARSE_KEY_DUPLICITY:
+               case EDG_WLL_ERROR_PARSE_KEY_MISUSE:
+//             case EDG_WLL_ERROR_PARSE_OK_WITH_EXTRA_FIELDS:
+                       edg_wll_UpdateError(context,EINVAL,"edg_wll_DoLogEvent(): Error code mapped to EINVAL");
+                       break;
+               case EDG_WLL_IL_PROTO:
+               case EDG_WLL_IL_SYS:
+               case EDG_WLL_IL_EVENTS_WAITING:
+                       edg_wll_UpdateError(context,EAGAIN,"edg_wll_DoLogEvent(): Error code mapped to EAGAIN");
+                       break;
+
+               default:
+                       edg_wll_UpdateError(context,EAGAIN,"edg_wll_DoLogEvent(): Error code mapped to EAGAIN");
+               break;
+       }
+
+edg_wll_DoLogEvent_end:
+       if (ssl) edg_wll_ssl_close_timeout(ssl,&context->p_tmp_timeout);
+       if (cred_handle) edg_wll_ssl_free(cred_handle);
+
+       return edg_wll_Error(context, NULL, NULL);
+}
+
+
+/**
+ *----------------------------------------------------------------------
+ * Formats a logging message and sends it to local-logger
+ * \brief master logging event function
+ * \param context      INOUT context to work with,
+ * \param priority     IN priority flag (0 for async, 1 for sync)
+ * \param event                IN type of the event,
+ * \param fmt          IN printf()-like format string,
+ * \param ...          IN event specific values/data according to fmt.
+ *----------------------------------------------------------------------
+ */
+static int edg_wll_LogEventMaster(
+       edg_wll_Context context,
+       int priority,
+       edg_wll_EventCode event,
+       char *fmt, ...)
+{
+       va_list fmt_args;
+       int     ret,answer;
+       char    *fix,*var;
+       char    *source,*eventName,*lvl, *fullid,*seq;
+        struct timeval start_time;
+       char    date[ULM_DATE_STRING_LENGTH+1];
+       edg_wll_LogLine out;
+       size_t  size;
+       int     i;
+
+       i = errno  =  size = 0;
+       seq = fix = var = out = source = eventName = lvl = fullid = NULL;
+
+       edg_wll_ResetError(context);
+
+   /* default return value is "Try Again" */
+       answer = ret = EAGAIN; 
+
+   /* format the message: */
+       va_start(fmt_args,fmt);
+
+       gettimeofday(&start_time,0);
+       if (edg_wll_ULMTimevalToDate(start_time.tv_sec,start_time.tv_usec,date) != 0) {
+               edg_wll_SetError(context,ret = EINVAL,"edg_wll_LogEventMaster(): edg_wll_ULMTimevalToDate() error"); 
+               goto edg_wll_logeventmaster_end; 
+       }
+       source = edg_wll_SourceToString(context->p_source);
+       lvl = edg_wll_LevelToString(context->p_level);
+       eventName = edg_wll_EventToString(event);
+       if (!eventName) { 
+               edg_wll_SetError(context,ret = EINVAL,"edg_wll_LogEventMaster(): event name not specified"); 
+               goto edg_wll_logeventmaster_end; 
+       }
+       if (!(fullid = edg_wlc_JobIdUnparse(context->p_jobid))) { 
+               edg_wll_SetError(context,ret = EINVAL,"edg_wll_LogEventMaster(): edg_wlc_JobIdUnparse() error"); 
+               goto edg_wll_logeventmaster_end;
+       }
+       seq = edg_wll_GetSequenceCode(context);
+       if (edg_wll_IncSequenceCode(context)) {
+               ret = EINVAL;
+               goto edg_wll_logeventmaster_end;
+       }
+       if (trio_asprintf(&fix,EDG_WLL_FORMAT_COMMON,
+                       date,context->p_host,lvl,priority,
+                       source,context->p_instance ? context->p_instance : "",
+                       eventName,fullid,seq) == -1) {
+               edg_wll_SetError(context,ret = ENOMEM,"edg_wll_LogEventMaster(): trio_asprintf() error"); 
+               goto edg_wll_logeventmaster_end; 
+       }
+       if (trio_vasprintf(&var,fmt,fmt_args) == -1) { 
+               edg_wll_SetError(context,ret = ENOMEM,"edg_wll_LogEventMaster(): trio_vasprintf() error"); 
+               goto edg_wll_logeventmaster_end; 
+       }
+       if (asprintf(&out,"%s%s\n",fix,var) == -1) { 
+               edg_wll_SetError(context,ret = ENOMEM,"edg_wll_LogEventMaster(): asprintf() error"); 
+               goto edg_wll_logeventmaster_end; 
+       }
+       size = strlen(out);
+
+       if (priority && (size > EDG_WLL_LOG_SYNC_MAXMSGSIZE)) {
+               edg_wll_SetError(context,ret = ENOSPC,"edg_wll_LogEventMaster(): Message size too large for synchronous transfer");
+               goto edg_wll_logeventmaster_end;
+       }
+
+#ifdef EDG_WLL_LOG_STUB
+//     fprintf(stderr,"edg_wll_LogEvent (%d chars): %s",size,out);
+#endif
+       
+       context->p_tmp_timeout.tv_sec = 0;
+       context->p_tmp_timeout.tv_usec = 0;
+       if (priority) {
+               context->p_tmp_timeout = context->p_sync_timeout;
+       }
+       else {
+               context->p_tmp_timeout = context->p_log_timeout;
+       }
+
+   /* and send the message to the local-logger: */
+       ret = edg_wll_DoLogEvent(context, /* priority,*/ out);
+
+edg_wll_logeventmaster_end:
+       va_end(fmt_args);
+       if (seq) free(seq); 
+       if (fix) free(fix); 
+       if (var) free(var); 
+       if (out) free(out);
+       if (source) free(source);
+       if (lvl) free(lvl);
+       if (eventName) free(eventName);
+       if (fullid) free(fullid);
+
+       if (ret) edg_wll_UpdateError(context,0,"Logging library ERROR: ");
+
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+/**
+ *----------------------------------------------------------------------
+ * Formats a logging message and sends it asynchronously to local-logger
+ * \brief generic asynchronous logging function
+ *----------------------------------------------------------------------
+ */
+int edg_wll_LogEvent(
+        edg_wll_Context context,
+        edg_wll_EventCode event,
+        char *fmt, ...)
+{
+       int     ret=0;
+       char    *list=NULL;
+       va_list fmt_args;
+
+       edg_wll_ResetError(context);
+
+       va_start(fmt_args,fmt);
+       if (trio_vasprintf(&list,fmt,fmt_args) == -1) { 
+               edg_wll_SetError(context,ret = ENOMEM,"edg_wll_LogEvent(): trio_vasprintf() error"); 
+               goto edg_wll_logevent_end; 
+       }
+
+       ret=edg_wll_LogEventMaster(context,0,event,"%s",list);
+
+edg_wll_logevent_end:
+       va_end(fmt_args);
+        if (list) free(list);
+
+       if (ret) edg_wll_UpdateError(context,0,"edg_wll_LogEvent(): ");
+
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+/**
+ *----------------------------------------------------------------------
+ * Formats a logging message and sends it synchronously to local-logger
+ * \brief generic synchronous logging function
+ * \note simple wrapper around edg_wll_LogEventMaster()
+ *----------------------------------------------------------------------
+ */
+int edg_wll_LogEventSync(
+        edg_wll_Context context,
+        edg_wll_EventCode event,
+        char *fmt, ...)
+{
+       int     ret=0;
+       char    *list=NULL;
+       va_list fmt_args;
+
+       edg_wll_ResetError(context);
+
+       va_start(fmt_args,fmt);
+       if (trio_vasprintf(&list,fmt,fmt_args) == -1) { 
+               edg_wll_SetError(context,ret = ENOMEM,"edg_wll_LogEventSync(): trio_vasprintf() error"); 
+               goto edg_wll_logeventsync_end; 
+       }
+
+       ret=edg_wll_LogEventMaster(context,1,event,"%s",list);
+
+edg_wll_logeventsync_end:
+       va_end(fmt_args);
+        if (list) free(list);
+
+       if (ret) edg_wll_UpdateError(context,0,"edg_wll_LogEventSync(): ");
+
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+
+/**
+ *-----------------------------------------------------------------------
+ * Instructs interlogger to to deliver all pending events related to current job
+ * \brief flush events from interlogger
+ * \note simple wrapper around edg_wll_LogEventMaster()
+ *-----------------------------------------------------------------------
+ */
+int edg_wll_LogFlush(
+        edg_wll_Context context,
+        struct timeval *timeout)
+{
+       int     ret = 0;
+       edg_wll_LogLine out = NULL;
+       char    *fullid;
+       char    date[ULM_DATE_STRING_LENGTH+1];
+        struct timeval start_time;
+
+       fullid = NULL;
+
+       edg_wll_ResetError(context);
+
+       gettimeofday(&start_time, 0);
+       if (edg_wll_ULMTimevalToDate(start_time.tv_sec, start_time.tv_usec, date) != 0) {
+               edg_wll_SetError(context,ret = EINVAL,"edg_wll_ULMTimevalToDate()"); 
+               goto edg_wll_logflush_end; 
+       }
+       if (!(fullid = edg_wlc_JobIdUnparse(context->p_jobid))) { 
+               ret = edg_wll_SetError(context,EINVAL,"edg_wlc_JobIdUnparse()");
+               goto edg_wll_logflush_end;
+       }
+
+       if (trio_asprintf(&out, "DATE=%s HOST=\"%|Us\" PROG=internal LVL=system DG.PRIORITY=1 DG.TYPE=\"command\" DG.COMMAND=\"flush\" DG.TIMEOUT=\"%d\" DG.JOBID=\"%s\"\n", 
+                   date, context->p_host, (timeout ? timeout->tv_sec : context->p_sync_timeout.tv_sec), fullid) == -1) {
+               edg_wll_SetError(context,ret = EINVAL,"trio_asprintf");
+               goto edg_wll_logflush_end;
+       }
+
+       if (timeout)
+               context->p_tmp_timeout = *timeout;
+       else
+               context->p_tmp_timeout = context->p_sync_timeout;
+
+       ret = edg_wll_DoLogEvent(context, /* 1,*/ out);
+
+edg_wll_logflush_end:
+       if(out) free(out);
+       if(fullid) free(fullid);
+       
+       if (ret) edg_wll_UpdateError(context,0,"edg_wll_LogFlush(): ");
+       
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+/**
+ *-----------------------------------------------------------------------
+ * Instructs interlogger to to deliver all pending events
+ * \brief flush all events from interlogger
+ *-----------------------------------------------------------------------
+ */
+int edg_wll_LogFlushAll(
+        edg_wll_Context context,
+        struct timeval *timeout)
+{
+       int     ret = 0;
+       edg_wll_LogLine out = NULL;
+       char    date[ULM_DATE_STRING_LENGTH+1];
+        struct timeval start_time;
+
+       edg_wll_ResetError(context);
+
+       gettimeofday(&start_time, 0);
+       if (edg_wll_ULMTimevalToDate(start_time.tv_sec, start_time.tv_usec, date) != 0) {
+               edg_wll_SetError(context,ret = EINVAL,"edg_wll_ULMTimevalToDate()"); 
+               goto edg_wll_logflushall_end; 
+       }
+
+       if (trio_asprintf(&out, "DATE=%s HOST=\"%|Us\" PROG=internal LVL=system DG.PRIORITY=1 DG.TYPE=\"command\" DG.COMMAND=\"flush\" DG.TIMEOUT=\"%d\"\n", 
+                   date, context->p_host, (timeout ? timeout->tv_sec : context->p_sync_timeout.tv_sec)) == -1) {
+               edg_wll_SetError(context,ret = ENOMEM,"trio_asprintf");
+               goto edg_wll_logflushall_end;
+       }
+
+       if (timeout)
+               context->p_tmp_timeout = *timeout;
+       else
+               context->p_tmp_timeout = context->p_sync_timeout;
+
+       ret = edg_wll_DoLogEvent(context, /* 1,*/ out);
+
+edg_wll_logflushall_end:
+       if(out) free(out);
+       
+       if (ret) edg_wll_UpdateError(context,0,"edg_wll_LogFlushAll(): ");
+       
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+/**
+ *-----------------------------------------------------------------------
+ * Set a current job for given context.
+ * \note Should be called before any logging call.
+ *-----------------------------------------------------------------------
+ */
+int edg_wll_SetLoggingJob(
+       edg_wll_Context context,
+       const edg_wlc_JobId job,
+       const char *code,
+       int flags)
+{
+       int     err;
+
+       edg_wll_ResetError(context);
+
+       if (!job) return edg_wll_SetError(context,EINVAL,"jobid is null");
+
+       edg_wlc_JobIdFree(context->p_jobid);
+       if ((err = edg_wlc_JobIdDup(job,&context->p_jobid)))
+               edg_wll_SetError(context,err,"edg_wll_SetLoggingJob(): edg_wlc_JobIdDup() error");
+
+       else {
+               if (!edg_wll_SetSequenceCode(context,code,flags))
+                       edg_wll_IncSequenceCode(context);
+       }
+
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+/**
+ *-----------------------------------------------------------------------
+ * Register job with L&B service.
+ *-----------------------------------------------------------------------
+ */
+static int edg_wll_RegisterJobMaster(
+        edg_wll_Context         context,
+       int                     pri,
+        const edg_wlc_JobId     job,
+        enum edg_wll_RegJobJobtype     type,
+        const char *            jdl,
+        const char *            ns,
+       edg_wlc_JobId           parent,
+        int                     num_subjobs,
+        const char *            seed,
+        edg_wlc_JobId **        subjobs)
+{
+       char    *type_s = NULL,*intseed = NULL, *seq = NULL;
+       char    *parent_s = NULL;
+       int     err = 0;
+
+       edg_wll_ResetError(context);
+
+       intseed = seed ? strdup(seed) : 
+               str2md5base64(seq = edg_wll_GetSequenceCode(context));
+
+       free(seq);
+
+       type_s = edg_wll_RegJobJobtypeToString(type);
+       if (!type_s) return edg_wll_SetError(context,EINVAL,"edg_wll_RegisterJobMaster(): no jobtype specified");
+
+       if ((type == EDG_WLL_REGJOB_DAG || type == EDG_WLL_REGJOB_PARTITIONED)
+               && num_subjobs > 0) 
+                       err = edg_wll_GenerateSubjobIds(context,job,
+                                       num_subjobs,intseed,subjobs);
+
+       parent_s = parent ? edg_wlc_JobIdUnparse(parent) : strdup("");
+
+       if (err == 0 &&
+               edg_wll_SetLoggingJob(context,job,NULL,EDG_WLL_SEQ_NORMAL) == 0)
+                       edg_wll_LogEventMaster(context,pri,
+                               EDG_WLL_EVENT_REGJOB,EDG_WLL_FORMAT_REGJOB,
+                               (char *)jdl,ns,parent_s,type_s,num_subjobs,intseed);
+
+       free(type_s); free(intseed); free(parent_s);
+       return edg_wll_Error(context,NULL,NULL);
+}
+
+int edg_wll_RegisterJobSync(
+        edg_wll_Context         context,
+        const edg_wlc_JobId     job,
+        enum edg_wll_RegJobJobtype     type,
+        const char *            jdl,
+        const char *            ns,
+        int                     num_subjobs,
+        const char *            seed,
+        edg_wlc_JobId **        subjobs)
+{
+       return edg_wll_RegisterJobMaster(context,1,job,type,jdl,ns, NULL, num_subjobs,seed,subjobs);
+}
+
+int edg_wll_RegisterJob(
+        edg_wll_Context         context,
+        const edg_wlc_JobId     job,
+        enum edg_wll_RegJobJobtype     type,
+        const char *            jdl,
+        const char *            ns,
+        int                     num_subjobs,
+        const char *            seed,
+        edg_wlc_JobId **        subjobs)
+{
+       return edg_wll_RegisterJobMaster(context,0,job,type,jdl,ns, NULL, num_subjobs,seed,subjobs);
+}
+
+int edg_wll_RegisterSubjob(
+        edg_wll_Context         context,
+        const edg_wlc_JobId     job,
+        enum edg_wll_RegJobJobtype     type,
+        const char *            jdl,
+        const char *            ns,
+       edg_wlc_JobId           parent,
+        int                     num_subjobs,
+        const char *            seed,
+        edg_wlc_JobId **        subjobs)
+{
+       return edg_wll_RegisterJobMaster(context,0,job,type,jdl,ns, parent, num_subjobs,seed,subjobs);
+}
+
+int edg_wll_RegisterSubjobs(edg_wll_Context ctx,const edg_wlc_JobId parent,
+               char const * const * jdls, const char * ns, edg_wlc_JobId const * subjobs)
+{
+       char const * const      *pjdl;
+       edg_wlc_JobId const     *psubjob;
+       edg_wlc_JobId           oldctxjob;
+       char *                  oldctxseq;
+
+       if (edg_wll_GetLoggingJob(ctx, &oldctxjob)) return edg_wll_Error(ctx, NULL, NULL);
+       oldctxseq = edg_wll_GetSequenceCode(ctx);
+
+       pjdl = jdls;
+       psubjob = subjobs;
+       
+       while (*pjdl != NULL) {
+               if (edg_wll_RegisterSubjob(ctx, *psubjob, EDG_WLL_REGJOB_SIMPLE, *pjdl,
+                                               ns, parent, 0, NULL, NULL) != 0) break;
+               pjdl++; psubjob++;
+       }
+
+       edg_wll_SetLoggingJob(ctx, oldctxjob, oldctxseq, EDG_WLL_SEQ_NORMAL);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+int edg_wll_ChangeACL(
+               edg_wll_Context                         ctx,
+               const edg_wlc_JobId                     jobid,
+               const char                                 *user_id,
+               enum edg_wll_UserIdType         user_id_type,
+               enum edg_wll_Permission         permission,
+               enum edg_wll_PermissionType     permission_type,
+               enum edg_wll_ACLOperation       operation)
+{
+       if ( edg_wll_SetLoggingJob(ctx, jobid, NULL, EDG_WLL_SEQ_NORMAL) == 0 )
+               edg_wll_LogEventMaster(ctx, 1, EDG_WLL_EVENT_CHANGEACL, EDG_WLL_FORMAT_CHANGEACL,
+                               user_id, user_id_type, permission, permission_type, operation);
+
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.client/src/purge.c b/org.glite.lb.client/src/purge.c
new file mode 100644 (file)
index 0000000..7d4ba32
--- /dev/null
@@ -0,0 +1,392 @@
+#ident "$Header$"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include <globus_common.h>
+
+#define CLIENT_SBIN_PROG
+
+#include "glite/wms/tls/ssl_helpers/ssl_inits.h"
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/xml_parse.h"
+#include "glite/lb/mini_http.h"
+
+
+#define dprintf(x) { if (debug) printf x; }
+
+#define free_jobs(jobs) {                                      \
+       if (jobs) {                                             \
+               int i;                                          \
+               for ( i = 0; jobs[i]; i++ )                     \
+                       free(jobs[i]);                          \
+               free(jobs);                                     \
+       }                                                       \
+}
+
+static const char rcsid[] = "@(#)$Id$";
+
+static int debug=0;
+static char *file;
+
+static int read_jobIds(const char *file, char ***jobs_out);
+static int get_timeout(const char *arg, int *timeout);
+static void printerr(edg_wll_Context ctx);
+
+static struct option opts[] = {
+       { "aborted",            required_argument, NULL, 'a'},
+       { "cleared",            required_argument, NULL, 'c'},
+       { "cancelled",          required_argument, NULL, 'n'},
+       { "other",              required_argument, NULL, 'o'},
+       { "dry-run",            no_argument, NULL, 'r'},
+       { "jobs",               required_argument, NULL, 'j'},
+       { "return-list",        no_argument, NULL, 'l'},
+       { "server-dump",        no_argument, NULL, 's'},
+       { "client-dump",        no_argument, NULL, 'i'},
+       { "help",               no_argument, NULL, 'h' },
+       { "version",            no_argument, NULL, 'v' },
+       { "debug",              no_argument, NULL, 'd' },
+       { "server",             required_argument, NULL, 'm' },
+       { NULL,                 no_argument, NULL,  0 }
+};
+
+static void usage(char *me)
+{
+       fprintf(stderr,"usage: %s [option]\n"
+               "       -a, --aborted NNN[smhd]     purge ABORTED jobs older than NNN secs/mins/hours/days\n"
+               "       -c, --cleared NNN[smhd]     purge CLEARED jobs older than given time\n"
+               "       -n, --cancelled NNN[smhd]   purge CANCELLED jobs older than given time\n"
+               "       -o, --other NNN[smhd]       purge OTHER jobs older than given time\n"
+               "       -r, --dry-run               do not really purge\n"
+               "       -j, --jobs <filename>       input file with jobIds of jobs to purge\n"
+               "       -l, --return-list           return list of jobid matching the purge/dump criteria\n"
+               "       -s, --server-dump           dump jobs into any server file\n"
+               "       -i, --client-dump           receive stream of dumped jobs\n"
+               "       -h, --help                  display this help\n"
+               "       -v, --version               display version\n"
+               "       -d, --debug                 diagnostic output\n"
+               "       -m, --server                L&B server machine name\n",
+               me);
+}
+
+int main(int argc,char *argv[])
+{
+       edg_wll_PurgeRequest *request;
+       edg_wll_PurgeResult *result;
+       int     i, timeout;
+       char *server = NULL;
+
+       char *me;
+       int opt;
+       edg_wll_Context ctx;
+
+       /* initialize request to server defaults */
+       request = (edg_wll_PurgeRequest *) calloc(1,sizeof(edg_wll_PurgeRequest));
+       request->jobs = NULL;
+       for (i=0; i < EDG_WLL_NUMBER_OF_STATCODES; i++) {
+               request->timeout[i]=-1;
+       }
+       request->flags = EDG_WLL_PURGE_REALLY_PURGE;
+
+       /* initialize result */
+       result = (edg_wll_PurgeResult *) calloc(1,sizeof(edg_wll_PurgeResult));
+
+       me = strrchr(argv[0],'/');
+       if (me) me++; else me=argv[0];
+
+       /* get arguments */
+       while ((opt = getopt_long(argc,argv,"a:c:n:o:j:m:rlsidhv",opts,NULL)) != EOF) {
+               timeout=-1;
+
+               switch (opt) {
+
+               case 'a': 
+                       if ((get_timeout(optarg,&timeout) != 0 )) {
+                               printf("Wrong usage of timeout argument.\n");
+                               usage(me);
+                               return 1;
+                       }
+                       if (timeout >= 0) {
+                               request->timeout[EDG_WLL_JOB_ABORTED]=timeout; 
+                       }
+                       break;
+
+               case 'c':
+                       if (get_timeout(optarg,&timeout) != 0 ) {
+                               printf("Wrong usage of timeout argument.\n");
+                               usage(me);
+                               return 1;
+                       }
+                       if (timeout >= 0) {
+                               request->timeout[EDG_WLL_JOB_CLEARED]=timeout; 
+                       }
+                       break;
+
+               case 'n': 
+                       if (get_timeout(optarg,&timeout) != 0 ) {
+                               printf("Wrong usage of timeout argument.\n");
+                               usage(me);
+                               return 1;
+                       }
+                       if (timeout >= 0) {
+                               request->timeout[EDG_WLL_JOB_CANCELLED]=timeout; 
+                       }
+                       break;
+               case 'o': 
+                       if (get_timeout(optarg,&timeout) != 0 ) {
+                               printf("Wrong usage of timeout argument.\n");
+                               usage(me);
+                               return 1;
+                       }
+                       if (timeout >= 0) request->timeout[EDG_WLL_PURGE_JOBSTAT_OTHER]= timeout;
+                       break;
+
+               case 'm': server = optarg; break;
+               case 'j': file = optarg; break;
+               case 'r': request->flags &= (~EDG_WLL_PURGE_REALLY_PURGE); break;
+               case 'l': request->flags |= EDG_WLL_PURGE_LIST_JOBS; break;
+               case 's': request->flags |= EDG_WLL_PURGE_SERVER_DUMP; break;
+               case 'i': request->flags |= EDG_WLL_PURGE_CLIENT_DUMP; break;
+               case 'd': debug = 1; break;
+               case 'v': fprintf(stdout,"%s:\t%s\n",me,rcsid); exit(0);
+               case 'h':
+               case '?': usage(me); return 1;
+               }
+       }
+
+       /* Initialize Globus common module */
+       dprintf(("Initializing Globus common module..."));
+       if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS) {
+               dprintf(("no.\n"));
+               fprintf(stderr,"Unable to initialize Globus common module\n");
+       } else {
+               dprintf(("yes.\n"));
+       }
+
+       edg_wlc_SSLInitialization();
+
+       /* initialize context */
+       edg_wll_InitContext(&ctx);
+
+       /* read the jobIds from file, if wanted */
+       if (file) {
+               char **jobs=NULL;
+               dprintf(("Reading jobIds form file \'%s\'...",file));
+               if (read_jobIds(file,&jobs) != 0) {
+                       dprintf(("no.\n"));
+                       fprintf(stderr,"Unable to read jobIds from file \'%s\'\n",file);
+                       goto main_end;
+               } else {
+                       dprintf(("yes.\n"));
+               }
+               request->jobs = jobs;
+       }
+
+       /* check request */
+       if (debug) {
+               printf("Purge request:\n");
+               printf("- flags: %d\n",request->flags);
+               printf("- %d timeouts:\n",EDG_WLL_NUMBER_OF_STATCODES);
+               for (i=0; i < EDG_WLL_NUMBER_OF_STATCODES; i++) {
+                       char *stat=edg_wll_StatToString(i);
+                       printf("\t%s: %ld\n",stat,request->timeout[i]);
+                       if (stat) free(stat);
+               }
+               printf("- list of jobs:\n");
+               if (!request->jobs) {
+                       printf("Not specified.\n");
+               } else {
+                       for ( i = 0; request->jobs[i]; i++ )
+                               printf("%s\n", request->jobs[i]);
+               }
+       }
+
+       if ( server )
+       {
+               char *p = strchr(server, ':');
+               if ( p )
+               {
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER_PORT, atoi(p+1));
+                       *p = 0;
+               }
+               edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER, server);
+       }
+
+       /* that is the Purge */
+       dprintf(("Running the edg_wll_Purge...\n"));
+       if (edg_wll_Purge(ctx, request, result) != 0) {
+               fprintf(stderr,"Error running the edg_wll_Purge().\n");
+               printerr(ctx);
+               switch ( edg_wll_Error(ctx, NULL, NULL) )
+               {
+               case ENOENT:
+               case EPERM:
+               case EINVAL:
+                       break;
+               default:
+                       goto main_end;
+               }
+       }
+
+       /* examine the result */
+       dprintf(("Examining the result of edg_wll_Purge...\n"));
+       if (result->server_file) {
+               printf("Server dump: %s\n",result->server_file);
+       } else {
+               printf("The jobs were not dumped.\n");
+       }
+       if (request->flags & EDG_WLL_PURGE_LIST_JOBS) {
+               printf("The following jobs %s purged:\n",
+                       request->flags & EDG_WLL_PURGE_REALLY_PURGE ? "were" : "would be");
+               if (!result->jobs) printf("None.\n");
+               else {
+                       int i;
+                       for ( i = 0; result->jobs[i]; i++ )
+                               printf("%s\n",result->jobs[i]);
+               }
+       }
+
+main_end:
+       dprintf(("End.\n"));
+       if (request)
+       {
+               free_jobs(request->jobs);
+               free(request);
+       }
+       if (result) free(result);
+       edg_wll_FreeContext(ctx);
+       return 0;
+}
+
+
+static void printerr(edg_wll_Context ctx) 
+{
+       char    *errt,*errd;
+
+       edg_wll_Error(ctx,&errt,&errd);
+       fprintf(stderr,"%s (%s)\n",errt,errd);
+}
+
+
+static int read_jobIds(const char *file, char ***jobs_out)
+{
+        FILE    *jobIds = fopen(file,"r");
+        char    buf[256];
+               char    **jobs;
+        int     cnt = 0;
+
+       jobs = NULL;
+
+
+        if (!jobIds) {
+                perror(file);
+                return 1;
+        }
+
+        while ( 1 ) {
+                char    *nl;
+                if ( !fgets(buf,sizeof buf,jobIds) )
+               {
+                       if (feof(jobIds))
+                               break;
+
+                       free_jobs(jobs);
+                       fprintf(stderr, "Error reading file\n");
+                       return 1;
+               }
+                nl = strchr(buf,'\n');
+                if (nl) *nl = 0;
+               /* TODO: check if it is really jobId, e.g. by edg_wlc_JobIdParse() */
+
+               if ( !(jobs = realloc(jobs, (cnt+2)*sizeof(*jobs))) )
+               {
+                       perror("cond_parse()");
+                       return(1);
+               }
+                jobs[cnt++] = strdup(buf);
+        }
+       jobs[cnt] = NULL;
+
+        fclose(jobIds);
+       *jobs_out = jobs;
+
+        return 0;
+}
+
+static int get_timeout(const char *arg, int *timeout) 
+{
+       int t = -1;
+       char tunit = '\0';
+
+       if (sscanf(arg,"%d%c",&t,&tunit) > 0) {
+               if (tunit) {
+                       switch (tunit) {
+                               case 'd': t *= 86400; break; // 24*60*60
+                               case 'h': t *= 3600; break; // 60*60
+                               case 'm': t *= 60; break;
+                               case 's': break;
+                               default: fprintf(stderr,"Allowed time units are s,m,h,d\n");
+                                       return -1;
+                       }
+               }
+       }
+       if (t < 0) return -1;
+       *timeout = t;
+       return 0;
+}
+
+static const char* const request_headers[] = {
+       "Cache-Control: no-cache",
+       "Accept: application/x-dglb",
+       "User-Agent: edg_wll_Api/" PROTO_VERSION "/" COMP_PROTO,
+       "Content-Type: application/x-dglb",
+       NULL
+};
+
+int edg_wll_Purge(
+               edg_wll_Context ctx,
+               edg_wll_PurgeRequest *request,
+               edg_wll_PurgeResult *result)
+{
+       char    *send_mess,
+               *response = NULL,
+               *recv_mess = NULL;
+
+       edg_wll_ResetError(ctx);
+
+       if (request->flags & EDG_WLL_PURGE_CLIENT_DUMP)
+               return edg_wll_SetError(ctx,ENOSYS,"client dump");
+
+       if (edg_wll_PurgeRequestToXML(ctx, request, &send_mess))
+               goto edg_wll_purge_end;
+
+       //edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_TIMEOUT, 4000);
+       ctx->p_tmp_timeout.tv_sec = 400;
+
+       if (set_server_name_and_port(ctx, NULL)) 
+               goto edg_wll_purge_end;
+
+       if (edg_wll_http_send_recv(ctx,
+               "POST /purgeRequest HTTP/1.1", request_headers, send_mess,
+               &response, NULL, &recv_mess)) 
+               goto edg_wll_purge_end;
+
+       if (http_check_status(ctx, response, &recv_mess))
+               goto edg_wll_purge_end;
+
+       if (edg_wll_ParsePurgeResult(ctx, recv_mess, result))
+               goto edg_wll_purge_end;
+
+edg_wll_purge_end:
+       if (response) free(response);
+       if (recv_mess) free(recv_mess);
+       if (send_mess) free(send_mess);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.client/src/uiwrap.c.T b/org.glite.lb.client/src/uiwrap.c.T
new file mode 100644 (file)
index 0000000..1e01840
--- /dev/null
@@ -0,0 +1,102 @@
+#ident "$Header$"
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/producer.h"
+
+@@@{
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $a = "(edg_wll_Context context";
+       my $b = "(context,EDG_WLL_EVENT_$tu,EDG_WLL_FORMAT_$tu";
+       my $decl = "";
+       my $free = "";
+       my $doc = qq{
+ * \\param context\tcontext to work with,
+};
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->getName;
+               my $ft;
+               my $bfn = $fn;
+                if ($f->{codes}) {
+#                        $ft = "enum edg_wll\_$t" . ucfirst $fn;
+                        $ft = "char *";
+                } else {
+                        $ft = $f->getType;
+                }
+               if ($ULMasString{$f->{type}}) {
+                       $decl .= "\tchar *s_$fn = ".$f->getType()."ToString($fn);\n";
+                       $free .= "\tfree(s_$fn);\n";
+                       $bfn = "s_$fn";
+               }
+               $ft = "const ".$ft;
+               my $fc = $f->getComment;
+               $a = $a . ", $ft $fn";
+               $b = $b . ", $bfn";
+               $doc = $doc . " * \\param $fn\t$fc\n";
+       }
+       $a = $a . ")";
+       $b = $b . ")";
+
+       gen qq{
+/*!
+ * \\fn int edg_wll_Log$t$a; 
+ * \\brief simple wrapper around edg_wll_LogEvent for event $t} . $doc . qq{ * \\see edg_wll_LogEvent\(\)
+ */
+};
+#      gen "\nextern int edg_wll_Log$t$a;\n\n";
+       gen qq{
+int edg_wll_Log$t$a
+\{
+$decl
+       return edg_wll_LogEvent$b;
+\}\n
+};
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->getName;
+               my $ft;
+                if ($f->{codes}) {
+#                        $ft = "enum edg_wll\_$t" . ucfirst $fn;
+                        $ft = "char *";
+                } else {
+                        $ft = $f->getType;
+                }
+               my $ftreg = $ft;
+               $ftreg =~ s/\*/\\\*/g;
+               $ftreg = "const ".$ftreg;
+               my $fc = $f->getComment;
+               if ($f->{codes}) {
+                       for (@{$f->{codes}}) {
+                               my $code = uc $_->{name};
+                               my $c = $a;
+                               my $d = $b;
+                               my $e = $doc;
+                               $c =~ s/, $ftreg $fn//g;
+                               $d =~ s/$fn/"$code"/g;
+                               $e =~ s/ \* \\param $fn\t$fc\n//g;
+                               gen qq{
+/*!
+ * \\fn int edg_wll_Log$t$code$c; 
+ * \\brief simple wrapper around edg_wll_LogEvent for event $t, $fn $code} . $e . qq{ * \\see edg_wll_LogEvent\(\)
+ */
+};
+#                              gen "\nextern int edg_wll_Log$t$code$c;\n\n";
+                               gen qq{
+int edg_wll_Log$t$code$c
+\{
+$decl;
+       return edg_wll_LogEvent$d;
+\}\n
+};
+                       }
+               }
+       }
+}
+@@@}
diff --git a/org.glite.lb.common/Makefile b/org.glite.lb.common/Makefile
new file mode 100644 (file)
index 0000000..99a1096
--- /dev/null
@@ -0,0 +1,69 @@
+include Makefile.inc
+
+
+VPATH:=${src}
+AT3:=perl -I${lbconfig} ${lbproject}/at3
+
+SUFFIXES = .T
+
+DEBUG:=-g -O0
+CFLAGS:=${DEBUG} -I${src} -I${repository}/${globus}/include/${globusflavour} \
+       -I${repository}/${globus}/include/${globusflavour}/openssl \
+       -I${repository}/${ares}/include -I${repository}/${expat}/include\
+       -I${stageinc} -I${interface} \
+       -DDATAGRID_EXTENSION
+
+LDFLAGS:=-L${stagelib}
+
+COMPILE:=libtool --mode=compile ${CC} ${CFLAGS}
+LINK:=libtool --mode=link ${CC} -rpath ${stagelib} ${LDFLAGS} 
+INSTALL:=libtool --mode=install install
+
+OBJS:=dgssl.o escape.o events.o mini_http.o query_rec.o status.o \
+       xml_conversions.o xml_parse.o ulm_parse.o param.o \
+       events_parse.o il_string.o il_int.o notifid.o \
+       il_log.o il_msg.o context.o trio.o strio.o
+LOBJS:=`echo ${OBJS} | sed 's/\.o/\.lo/g'`
+
+HDRS:=context-int.h  dgssl.h  mini_http.h authz.h xml_parse.h \
+       xml_conversions.h log_proto.h events_parse.h il_string.h escape.h \
+       ulm_parse.h trio.h
+
+STATICLIB:=libglite_lb_common.a
+LTLIB:=libglite_lb_common.la
+
+default: all
+
+compile: ${STATICLIB} ${LTLIB}
+
+${STATICLIB}: ${OBJS}
+       ar crv $@ ${OBJS}
+       ranlib $@
+
+${LTLIB}: ${OBJS}
+       ${LINK} -o $@ ${LOBJS} \
+               -L${stagelib} -lglobus_ssl_utils -lglite_wms_cjobid \
+               -lm
+
+stage export all: compile
+       ${INSTALL} -m 644 ${STATICLIB} ${stagelib}
+       ${INSTALL} -m 644 ${LTLIB} ${stagelib}
+       cd ${interface} && install -m 644 ${HDRS} ${stageinc}/${globalprefix}/${lbprefix}
+
+
+check:
+       echo Unit tests missing
+
+%.o: %.c
+       ${COMPILE} -c $<
+
+
+%.h: %.h.T
+       rm -f $@
+       ${AT3} $< >$@ || rm -f $@
+       chmod -w $@ >/dev/null
+
+%.c: %.c.T
+       rm -f $@
+       ${AT3} $< >$@ || rm -f $@
+       chmod -w $@ >/dev/null
diff --git a/org.glite.lb.common/build.xml b/org.glite.lb.common/build.xml
new file mode 100755 (executable)
index 0000000..4aa4105
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="lb" default="dist">
+       
+       <import file="../org.glite/project/baseline.properties.xml" />
+       <import file="./project/properties.xml"/>
+       <import file="${subsystem.properties.file}"/>
+       <import file="${global.properties.file}" />
+
+       <property file="${user.dependencies.file}"/>
+       <property file="${component.dependencies.file}" />
+       <property file="${subsystem.dependencies.file}" />
+       <property file="${global.dependencies.file}"/>
+       
+       <import file="${subsystem.taskdefs.file}" />
+       <import file="${global.taskdefs.file}" />
+
+       <import file="${global.targets-external-dependencies.file}"/>   
+       <import file="${global.targets-make.file}" />
+               
+       <property file="${module.version.file}"/>
+
+       <target name="localinit"
+               description="Module specific initialization tasks">
+               <antcall target="lbmakefiles"/>
+       </target>
+
+       <target name="localcompile"
+               description="Module specific compile tasks">
+       </target>
+       
+       <target name="localclean"
+               description="Module specific cleaning tasks">
+       </target>
+       
+</project>             
diff --git a/org.glite.lb.common/interface/authz.h b/org.glite.lb.common/interface/authz.h
new file mode 100644 (file)
index 0000000..ccb02ad
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef _LB_AUTHZ_H
+#define _LB_AUTHZ_H
+
+#ident "$Header$"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct _edg_wll_VomsGroup {
+       char *vo;
+       char *name;
+} edg_wll_VomsGroup;
+
+typedef struct _edg_wll_VomsGroups {
+       size_t len;
+       edg_wll_VomsGroup *val;
+} edg_wll_VomsGroups;
+
+#ifdef __cplusplus 
+}
+#endif
+
+#endif
diff --git a/org.glite.lb.common/interface/context-int.h b/org.glite.lb.common/interface/context-int.h
new file mode 100644 (file)
index 0000000..e7a2954
--- /dev/null
@@ -0,0 +1,152 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_CONTEXT_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_CONTEXT_H__
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+#include "dgssl.h"
+#include "authz.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+       
+typedef struct _edg_wll_SeqCode {
+       unsigned int    c[EDG_WLL_SOURCE__LAST];
+} edg_wll_SeqCode;
+
+
+
+typedef struct _edg_wll_ConnPool {
+/* address and port where we are connected to */
+       char            *peerName;
+       unsigned int    peerPort;
+       
+/* http(s) stream */
+       void            *gsiCred;
+       SSL             *ssl;
+       char            *buf;
+       int             bufUse,bufSize;
+
+/* timestamp of usage of this entry in ctx.connPool */
+       struct timeval  lastUsed;
+} edg_wll_ConnPool;
+
+
+
+struct _edg_wll_Context {
+/* Error handling */
+       int             errCode;        /* recent error code */
+       char            *errDesc;       /* additional error description */
+
+/* server part */
+
+       void            *mysql;
+       edg_wll_ConnPool        *connPool;
+
+       int             semaphores,semset;
+       edg_wll_QueryRec        **job_index;
+       void            *job_index_cols;
+       
+       time_t          peerProxyValidity;
+       char            *peerName;
+       edg_wll_VomsGroups      vomsGroups;
+       int             allowAnonymous;
+       int             noAuth;         /* if set, you can obtain info about events     */
+                                       /* and jobs not belonging to you                */
+       int             noIndex;        /* don't enforce indices */
+       int             rgma_export;
+       int             strict_locking; /* lock jobs for storing event too */
+
+       
+/* server limits */
+       int             softLimit;
+       int             hardJobsLimit;
+       int             hardEventsLimit;
+
+       time_t          notifDuration;
+
+/* purge and dump files storage */
+       char            *dumpStorage;
+       char            *purgeStorage;
+
+/* flag for function store_event
+ * if set then event are loaded from dump file
+ */
+       int             event_load;
+
+/* address and port we are listening at */
+       char            *srvName;
+       unsigned int    srvPort;
+       
+/* pool of connections from client */
+       int             poolSize;
+       int             connOpened;     /* number of opened connections  */
+       int             connToUse;      /* index of connection that will *
+                                        *  be used by low-level f-cions */
+/* other client stuff */
+       int             notifSock;              /* default client socket        *
+                                                * for receiving notifications  */   
+       
+/* user settable parameters */
+       char            *p_host;
+       edg_wll_Source  p_source;
+       char            *p_instance;
+       enum edg_wll_Level      p_level;
+       char            *p_destination;
+       int             p_dest_port;
+       struct timeval  p_log_timeout,p_sync_timeout,p_query_timeout, p_notif_timeout, p_tmp_timeout;
+       char            *p_query_server;
+       int             p_query_server_port;
+       int             p_query_server_override;
+       int             p_query_events_limit;
+       int             p_query_jobs_limit;
+       edg_wll_QueryResults    p_query_results;
+       char            *p_notif_server;
+       int             p_notif_server_port;
+       char            *p_proxy_filename;
+       char            *p_cert_filename;
+       char            *p_key_filename;
+       time_t          purge_timeout[EDG_WLL_NUMBER_OF_STATCODES];
+/* producer part */
+       edg_wlc_JobId   p_jobid;
+       edg_wll_SeqCode p_seqcode;
+};
+
+/* to be used internally: set, update and and clear the error information in 
+ * context, the desc string (if provided) is strdup()-ed
+ *
+ * all return the error code for convenience (usage in return statement) */
+
+
+extern int edg_wll_SetError(
+       edg_wll_Context,        /* context */
+       int,                    /* error code */
+       const char *            /* error description */
+);
+
+/** update errDesc and errCode */
+extern int edg_wll_UpdateError(
+       edg_wll_Context,        /* context */
+       int,                    /* error code */
+       const char *            /* error description */
+);
+
+/** set errCode to 0, free errDesc */
+extern int edg_wll_ResetError(edg_wll_Context);
+
+/** retrieve standard error text wrt. code
+ * !! does not allocate memory */
+extern const char *edg_wll_GetErrorText(int);
+
+extern int edg_wll_ContextReopen(edg_wll_Context);
+
+extern int edg_wll_SetSequenceCode(edg_wll_Context, const char *, int);
+extern int edg_wll_IncSequenceCode(edg_wll_Context ctx);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_CONTEXT_H__ */
diff --git a/org.glite.lb.common/interface/dgssl.h b/org.glite.lb.common/interface/dgssl.h
new file mode 100644 (file)
index 0000000..1de1ec4
--- /dev/null
@@ -0,0 +1,204 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_DGSSL_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_DGSSL_H__
+/**
+ * \file Client/dgssl.h
+ * \brief Auxiliary SSL functions for L&B 
+ */
+
+#ident "$Header$"
+
+/* openssl headers */
+#include <globus_config.h>
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include <ssl.h>
+#include <err.h>
+#include <rand.h>
+#include <bio.h>
+#include <pem.h>
+#include <x509.h>
+#if SSLEAY_VERSION_NUMBER >= 0x0090581fL
+#include <x509v3.h>
+#endif
+
+/**
+ * Initialize SSL and GSI contexts and set SSL parameters for authentication.
+ * \param verify IN: specifies SSL authentication mode to be used for peer 
+ * verification. (SSL_VERIFY_NONE, SSL_VERIFY_PEER, 
+ * SSL_VERIFY_FAIL_IF_NO_PEER_CERT, SSL_VERIFY_CLIENT_ONCE) -- see also ssl.h
+ * \param callback IN: if nonzero the standard GSI authentication callback will
+ * be called on each certificate in a chain in order to verify the chain
+ * consists only of valid GSI proxy certs. If the parametr is zero only the
+ * default SSL callback will be used.
+ * \param p_cert_file IN: file containg the caller's cert.
+ * \param p_key_file IN: file containg the caller's key. If at least one of
+ * these parameters is NULL, the default proxy is examined and used (if
+ * exists), otherwise the default long-term credential will be used.  \param
+ * ask_passwd IN: if nonzero, the standard SSL prompt is used whenever an
+ * encrypted keyfile is encountered. If the paramater zero, no password will be
+ * prompted for even if the key is encrypted.
+ * \param no_auth IN: if nonzero, only context necessary for authentication of
+ * the peer will be set. No caller's credential is read.
+ * \retval pointer to an opaque structure containg the initialized context, if
+ * any error occurs, the context will contain only those basic parameters
+ * necessary for authentication of the peer
+ */
+proxy_cred_desc *edg_wll_ssl_init(int verify, int callback, char *p_cert_file,char *p_key_file,
+                  int ask_passwd, int noauth);
+
+/**
+ * frees cred_handle returned by edg_wll_ssl_init()
+ * \param cred_handle IN: pointer returned by edg_wll_ssl_init()
+ */
+int edg_wll_ssl_free(proxy_cred_desc *cred_handle);
+
+/**
+ * Establish SSL context with server.
+ * Called by the clients when they tryies to do SSL handshake with the server.
+ * This includes authentication of the server and optionaly the client, and
+ * establishing SSL security parameters.
+ *
+ * \param cred_handle IN: pointer returned by edg_wll_ssl_init()
+ * \param hostname IN: hostname to connect to
+ * \param port IN: port to connect to
+ * \param timeout INOUT: if NULL, blocking SSL I/O is done, otherwise the
+ * I/O and SSL handshake will take at most the specified time. Remaining
+ * time is returned.
+ * \param sslp OUT: on success, returned pointer to SSL connection
+ * \return one of EDG_WLL_SSL_*
+ */
+int edg_wll_ssl_connect(proxy_cred_desc *cred_handle,char const *hostname,int port,
+        struct timeval *timeout,SSL **sslp);
+
+/**
+ * Establish SSL context with client.
+ * Called by the server when trying to do a SSL handshake with the client. A
+ * network connection must be established before calling this function. Use
+ * timeout set in context.
+ * \param ctx IN: used to obtain timeout
+ * \param cred_handle IN: pointer returned by edg_wll_ssl_init()
+ * \param sock IN: specification of the network connection
+ * \retval pointer to SSL connection on success
+ * \retval NULL on error
+ */
+SSL *edg_wll_ssl_accept(proxy_cred_desc *cred_handle,int sock, struct timeval *timeout);
+
+/**
+ * Try to send SSL close alert on socket (reject the client in orderly way).
+ * \param cred_handle IN: pointer returned by edg_wll_ssl_init(), to get SSL_CTX
+ * \param sock IN: specification of the network connection
+ */
+
+void edg_wll_ssl_reject(proxy_cred_desc *cred_handle,int sock);
+
+/**
+ * Close SSL connection, possibly saving open sessions, and destroy the SSL object.
+ * Called by anyone who wishes to close the connection.
+ * \param ssl IN: object identifying the SSL connection
+ * \param timeout INOUT: max time allowed for operation, remaining time
+ * on return
+ * \retval one of EDG_WLL_SSL_*
+ */
+int edg_wll_ssl_close_timeout(SSL *ssl, struct timeval *timeout);
+
+/**
+ * Close SSL connection, call edg_wll_ssl_close_timeout() with hard-wired
+ * timeout 120 secs.
+ * \param ssl IN: object identifying the SSL connection
+ * \retval one of EDG_WLL_SSL_*
+ */
+int edg_wll_ssl_close(SSL *ssl);
+
+/**
+ * Return subject name of the caller as specified in the context.
+ * \param cred_handle IN: pointer returned by edg_wll_ssl_init()
+ * \param my_subject_name OUT: contains the subject name. The caller must free
+ * this variable when no needed.
+ */
+void edg_wll_ssl_get_my_subject(proxy_cred_desc *cred_handle, char **my_subject_name);
+void edg_wll_ssl_get_my_subject_base(proxy_cred_desc *cred_handle, char **my_subject_name);
+
+/** no SSL errors */
+#define EDG_WLL_SSL_OK         0
+/** SSL specific error. Call ERR_* functions for details. */
+#define EDG_WLL_SSL_ERROR_SSL          -1
+/** Timeout */
+#define EDG_WLL_SSL_ERROR_TIMEOUT      -2
+/** EOF occured */
+#define EDG_WLL_SSL_ERROR_EOF          -3
+/** System error. See errno. */
+#define EDG_WLL_SSL_ERROR_ERRNO                -4
+/** Resolver error. See h_errno. */
+#define EDG_WLL_SSL_ERROR_HERRNO       -5
+
+/**
+ * Read from SSL connection.
+ * Needn't read entire buffer. Timeout is applicable only for non-blocking
+ * connections (created with non-NULL timeout param of edg_wll_ssl_connect()).
+ * \param ssl IN: connection to work with
+ * \param buf OUT: buffer
+ * \param bufsize IN: max size to read
+ * \param timeout INOUT: max time allowed for operation, remaining time
+ * on return
+ * \retval bytes read (>0) on success
+ * \retval one of EDG_WLL_SSL_* (<0) on error
+ */
+
+int edg_wll_ssl_read(SSL *ssl,void *buf,size_t bufsize,struct timeval *timeout);
+
+/**
+ * Write to SSL connection.
+ * Needn't write entire buffer. Timeout is applicable only for non-blocking
+ * connections (created with non-NULL timeout param of edg_wll_ssl_connect()).
+ * \param ssl IN: connection to work with
+ * \param buf IN: buffer
+ * \param bufsize IN: max size to write
+ * \param timeout INOUT: max time allowed for operation, remaining time
+ * on return
+ * \retval bytes written (>0) on success
+ * \retval one of EDG_WLL_SSL_* (<0) on error
+ */
+
+int edg_wll_ssl_write(SSL *ssl,const void *buf,size_t bufsize,struct timeval *timeout);
+
+/**
+ * Read specified amount of data from SSL connection.
+ * Attempts to call edg_wll_ssl_read() untill the entire request is satisfied
+ * (or EOF reached, or times out)
+ *
+ * \param ssl IN: connection to work with
+ * \param buf OUT: buffer
+ * \param bufsize IN: max size to read
+ * \param timeout INOUT: max time allowed for operation, remaining time
+ * on return
+ * \param total OUT: bytes actually read
+ * \return one of EDG_WLL_SSL_*
+ */
+
+int edg_wll_ssl_read_full(SSL *ssl,void *buf,size_t bufsize,struct timeval *timeout,size_t *total);
+
+/**
+ * Write specified amount of data to SSL connection.
+ * Attempts to call edg_wll_ssl_write() untill the entire request is satisfied
+ * (or times out).
+ *
+ * \param ssl IN: connection to work with
+ * \param buf IN: buffer
+ * \param bufsize IN: max size to read
+ * \param timeout INOUT: max time allowed for operation, remaining time
+ * on return
+ * \param total OUT: bytes actually written
+ * \return one of EDG_WLL_SSL_*
+ */
+
+int edg_wll_ssl_write_full(SSL *ssl,const void *buf,size_t bufsize,struct timeval *timeout,size_t *total);
+
+void edg_wll_ssl_set_noauth(proxy_cred_desc* cred_handle);
+
+/**
+ * Check modification times of key and cert files.
+ */
+int edg_wll_ssl_watch_creds(const char *,const char *,time_t *,time_t *);
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_DGSSL_H__ */
+
diff --git a/org.glite.lb.common/interface/escape.h b/org.glite.lb.common/interface/escape.h
new file mode 100644 (file)
index 0000000..4cf54c0
--- /dev/null
@@ -0,0 +1,60 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_ESCAPE_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_ESCAPE_H__
+/*!
+ * \file Client/escape.h
+ * \brief Prototypes for Client/escape.c
+ */
+
+#ident "$Header$"
+
+
+/*!
+ * \fn char *edg_wll_LogEscape(const char *str)
+ * \param str           a string to escape
+ * \return              new (allocated) escaped string
+ * \brief in given string (ULM) escape all ULM_QM, ULM_BS and ULM_LF by ULM_BS
+ */
+
+char *edg_wll_LogEscape(const char *);
+
+
+/*!
+ * \fn char *edg_wll_LogUnescape(const char *str)
+ * \param str           a string to unescape
+ * \return              new (allocated) unescaped string
+ * \brief in given string (ULM) unescape all escaped ULM_QM, ULM_BS and ULM_LF
+ */
+
+char *edg_wll_LogUnescape(const char *);
+
+
+/*!
+ * \fn char *edg_wll_EscapeXML(const char *str);
+ * \param str          a string to escape
+ * \return             new (allocated) escaped string
+ * \brief in given string (XML) escape all unwanted characters
+ */
+
+char *edg_wll_EscapeXML(const char *);
+
+
+/*!
+ * \fn char *edg_wll_UnescapeXML(const char *str)
+ * \param str          a string to unescape
+ * \return             new (allocated) unescaped string
+ * \brief in given string (XML) unescape all escaped characters
+ */
+
+char *edg_wll_UnescapeXML(const char *);
+
+
+/*!
+ * \fn char *edg_wll_EscapeSQL(const char *str)
+ * \param str          a string to escape
+ * \return             new (allocated) escaped string
+ * \briefin given string (SQL) escape all unwanted characters
+ */
+
+char *edg_wll_EscapeSQL(const char *);
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_ESCAPE_H__ */
diff --git a/org.glite.lb.common/interface/events_parse.h b/org.glite.lb.common/interface/events_parse.h
new file mode 100644 (file)
index 0000000..d4ed8d7
--- /dev/null
@@ -0,0 +1,83 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_EVENTS_PARSE_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_EVENTS_PARSE_H__
+
+#ident "$Header$"
+
+#include "glite/lb/events.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * Parse a ULM line into a edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param logline IN: ULM string to parse
+ * \param event OUT: parsed event
+ *     (may be NULL - syntax checking with no output)
+ */
+extern edg_wll_ErrorCode edg_wll_ParseEvent(
+       edg_wll_Context context,
+       edg_wll_LogLine logline,
+       edg_wll_Event ** event
+);
+
+/** 
+ * Generate ULM line from edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param event IN: event to unparse
+ */
+extern edg_wll_LogLine edg_wll_UnparseEvent(
+       edg_wll_Context context,
+       edg_wll_Event * event
+);
+
+/**
+ * Check event for completness.
+ * Auxiliary function, checks whether all required fields of
+ * the event type are present.
+ * \param context IN: context to work with
+ * \param event IN: event to check
+ */
+extern edg_wll_ErrorCode edg_wll_CheckEvent(
+       edg_wll_Context context,
+       edg_wll_Event * event
+);
+
+/**
+ * Parse "only" jobId from ULM message
+ * \param logline IN: ULM string to parse
+ * \return jobId string
+ */
+extern char *edg_wll_GetJobId(edg_wll_LogLine logline);
+
+/**
+ * Parse a special Notification ULM line into a edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param logline IN: ULM string to parse
+ * \param event OUT: parsed event
+ *     (may be NULL - syntax checking with no output)
+ */
+extern edg_wll_ErrorCode edg_wll_ParseNotifEvent(
+       edg_wll_Context context,
+       edg_wll_LogLine logline,
+       edg_wll_Event ** event
+);
+
+/** 
+ * Generate a special Notification ULM line from edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param event IN: event to unparse
+ */
+extern edg_wll_LogLine edg_wll_UnparseNotifEvent(
+       edg_wll_Context context,
+       edg_wll_Event * event
+);
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_EVENTS_PARSE_H__ */
diff --git a/org.glite.lb.common/interface/il_string.h b/org.glite.lb.common/interface/il_string.h
new file mode 100644 (file)
index 0000000..db480f7
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef NET_STRING_H
+#define NET_STRING_H
+
+#ident "$Header$"
+
+#define MAXLEN 1024
+
+char *put_int(char *p, int d);
+char *_put_int(char *p, int d);
+char *put_string(char *p, char *s);
+
+char *get_int(char *p, int *d);
+char *_get_int(char *p, int *d);
+char *get_string(char *p, char **s);
+
+int len_string(char *s);
+int len_int(int d);
+
+enum {
+  LB_OK    = 0,
+  LB_NOMEM = 200,
+  LB_PROTO = 400,
+  LB_AUTH  = 500,
+  LB_PERM  = 600,
+  LB_DBERR = 700,
+  LB_SYS   = 800,
+  LB_TIME  = 900
+};
+
+#endif
diff --git a/org.glite.lb.common/interface/log_proto.h b/org.glite.lb.common/interface/log_proto.h
new file mode 100644 (file)
index 0000000..a2a59a8
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_LOG_PROTO_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_LOG_PROTO_H__
+
+#ident "$Header$"
+
+/**
+ * \file edg/workload/logging/common/log_proto.h
+ * \brief common part of the logging protocol
+ * \note private
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/**
+ * default (noauth) user name
+ */
+/** default user */
+#define EDG_WLL_LOG_USER_DEFAULT                "anonymous"
+
+
+/**
+ * default prefix for names of log files 
+ */
+/** default prefix */
+#define EDG_WLL_LOG_PREFIX_DEFAULT              "/tmp/dglogd.log"
+
+
+/** 
+ * default local-logger Socket header 
+ */
+/** header text */
+#define EDG_WLL_LOG_SOCKET_HEADER               "DGLOG"
+/** header length */
+#define EDG_WLL_LOG_SOCKET_HEADER_LENGTH        5
+
+
+/**
+ * default local-logger destination 
+ */
+/** host */
+#define EDG_WLL_LOG_HOST_DEFAULT               "localhost"
+/** port */
+#define EDG_WLL_LOG_PORT_DEFAULT               9002
+
+
+/**
+ * default and maximal logging timeout (in seconds)
+ */
+#define EDG_WLL_LOG_TIMEOUT_DEFAULT            120
+#define EDG_WLL_LOG_TIMEOUT_MAX                        1800
+#define EDG_WLL_LOG_SYNC_TIMEOUT_DEFAULT       120
+#define EDG_WLL_LOG_SYNC_TIMEOUT_MAX           1800
+
+
+/**
+ * maximal message size for sync logging
+ */
+/** max message size in bytes */
+// unlimited for tests!
+#define EDG_WLL_LOG_SYNC_MAXMSGSIZE            102400000
+
+       
+/**
+ * default maximal number of simultaneously open connections from one client
+ */
+#define EDG_WLL_LOG_CONNECTIONS_DEFAULT                4
+
+       
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_LOG_PROTO_H__ */
diff --git a/org.glite.lb.common/interface/mini_http.h b/org.glite.lb.common/interface/mini_http.h
new file mode 100644 (file)
index 0000000..beea6e7
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_MINI_HTTP_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_MINI_HTTP_H__
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+
+/* XXX: not a good place for the folowing #def's but we ain't got better currently */
+/** protocol version */
+#define PROTO_VERSION           "3.0"
+/** backward protocol compatibility     */
+/* version separated by comma           */
+/* e.g. "1.0,1.2,1.3"                   */
+#define COMP_PROTO              "3.0"
+
+
+/* subset of HTTP codes we return */
+#define HTTP_OK                        200
+#define HTTP_BADREQ            400
+#define HTTP_UNAUTH            401
+#define HTTP_NOTFOUND          404
+#define HTTP_NOTALLOWED                405
+#define HTTP_UNSUPPORTED       415 
+#define HTTP_INTERNAL          500
+#define HTTP_NOTIMPL           501
+#define HTTP_UNAVAIL           503
+#define HTTP_INVALID           579
+
+extern edg_wll_ErrorCode edg_wll_http_recv(
+       edg_wll_Context,        /* INOUT: context */
+       char **,        /* OUT: first line */
+       char ***,       /* OUT: null terminated array of headers */
+       char **         /* OUT: message body */
+);
+
+extern edg_wll_ErrorCode edg_wll_http_send(
+       edg_wll_Context,        /* INOUT: context */
+       const char *,           /* IN: first line */
+       char const * const *,   /* IN: headers */
+       const char *            /* IN: message body */
+);
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_MINI_HTTP_H__ */
diff --git a/org.glite.lb.common/interface/trio.h b/org.glite.lb.common/interface/trio.h
new file mode 100644 (file)
index 0000000..04f133c
--- /dev/null
@@ -0,0 +1,187 @@
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ ************************************************************************/
+
+#ifndef TRIO_TRIO_H
+#define TRIO_TRIO_H
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* make utility and C++ compiler in Windows NT fails to find this symbol */ 
+#if defined(WIN32) && !defined(isascii)
+# define isascii ((unsigned)(x) < 0x80)
+#endif
+
+/*
+ * Error codes.
+ *
+ * Remember to add a textual description to trio_strerror.
+ */
+enum {
+  TRIO_EOF      = 1,
+  TRIO_EINVAL   = 2,
+  TRIO_ETOOMANY = 3,
+  TRIO_EDBLREF  = 4,
+  TRIO_EGAP     = 5,
+  TRIO_ENOMEM   = 6,
+  TRIO_ERANGE   = 7
+};
+
+/* Error macros */
+#define TRIO_ERROR_CODE(x) ((-(x)) & 0x00FF)
+#define TRIO_ERROR_POSITION(x) ((-(x)) >> 8)
+#define TRIO_ERROR_NAME(x) trio_strerror(x)
+
+const char *trio_strerror(int);
+
+/*************************************************************************
+ * Print Functions
+ */
+
+int trio_printf(const char *format, ...);
+int trio_vprintf(const char *format, va_list args);
+int trio_printfv(const char *format, void **args);
+
+int trio_fprintf(FILE *file, const char *format, ...);
+int trio_vfprintf(FILE *file, const char *format, va_list args);
+int trio_fprintfv(FILE *file, const char *format, void **args);
+
+int trio_dprintf(int fd, const char *format, ...);
+int trio_vdprintf(int fd, const char *format, va_list args);
+int trio_dprintfv(int fd, const char *format, void **args);
+
+/* trio_sprintf(target, format, ...)
+ * trio_snprintf(target, maxsize, format, ...)
+ *
+ *   Build 'target' according to 'format' and succesive
+ *   arguments. This is equal to the sprintf() and
+ *   snprintf() functions.
+ */
+int trio_sprintf(char *buffer, const char *format, ...);
+int trio_vsprintf(char *buffer, const char *format, va_list args);
+int trio_sprintfv(char *buffer, const char *format, void **args);
+
+int trio_snprintf(char *buffer, size_t max, const char *format, ...);
+int trio_vsnprintf(char *buffer, size_t bufferSize, const char *format,
+                  va_list args);
+int trio_snprintfv(char *buffer, size_t bufferSize, const char *format,
+                  void **args);
+
+int trio_snprintfcat(char *buffer, size_t max, const char *format, ...);
+int trio_vsnprintfcat(char *buffer, size_t bufferSize, const char *format,
+                      va_list args);
+
+char *trio_aprintf(const char *format, ...);
+char *trio_vaprintf(const char *format, va_list args);
+
+int trio_asprintf(char **ret, const char *format, ...);
+int trio_vasprintf(char **ret, const char *format, va_list args);
+
+/*************************************************************************
+ * Scan Functions
+ */
+int trio_scanf(const char *format, ...);
+int trio_vscanf(const char *format, va_list args);
+int trio_scanfv(const char *format, void **args);
+
+int trio_fscanf(FILE *file, const char *format, ...);
+int trio_vfscanf(FILE *file, const char *format, va_list args);
+int trio_fscanfv(FILE *file, const char *format, void **args);
+
+int trio_dscanf(int fd, const char *format, ...);
+int trio_vdscanf(int fd, const char *format, va_list args);
+int trio_dscanfv(int fd, const char *format, void **args);
+
+int trio_sscanf(const char *buffer, const char *format, ...);
+int trio_vsscanf(const char *buffer, const char *format, va_list args);
+int trio_sscanfv(const char *buffer, const char *format, void **args);
+
+/*************************************************************************
+ * Renaming
+ */
+#ifdef TRIO_REPLACE_STDIO
+/* Replace the <stdio.h> functions */
+#ifndef HAVE_PRINTF
+# define printf trio_printf
+#endif
+#ifndef HAVE_VPRINTF
+# define vprintf trio_vprintf
+#endif
+#ifndef HAVE_FPRINTF
+# define fprintf trio_fprintf
+#endif
+#ifndef HAVE_VFPRINTF
+# define vfprintf trio_vfprintf
+#endif
+#ifndef HAVE_SPRINTF
+# define sprintf trio_sprintf
+#endif
+#ifndef HAVE_VSPRINTF
+# define vsprintf trio_vsprintf
+#endif
+#ifndef HAVE_SNPRINTF
+# define snprintf trio_snprintf
+#endif
+#ifndef HAVE_VSNPRINTF
+# define vsnprintf trio_vsnprintf
+#endif
+#ifndef HAVE_SCANF
+# define scanf trio_scanf
+#endif
+#ifndef HAVE_VSCANF
+# define vscanf trio_vscanf
+#endif
+#ifndef HAVE_FSCANF
+# define fscanf trio_fscanf
+#endif
+#ifndef HAVE_VFSCANF
+# define vfscanf trio_vfscanf
+#endif
+#ifndef HAVE_SSCANF
+# define sscanf trio_sscanf
+#endif
+#ifndef HAVE_VSSCANF
+# define vsscanf trio_vsscanf
+#endif
+/* These aren't stdio functions, but we make them look similar */
+#define dprintf trio_dprintf
+#define vdprintf trio_vdprintf
+#define aprintf trio_aprintf
+#define vaprintf trio_vaprintf
+#define asprintf trio_asprintf
+#define vasprintf trio_vasprintf
+#define dscanf trio_dscanf
+#define vdscanf trio_vdscanf
+#endif
+
+/* strio compatible names */
+#define StrScan trio_sscanf
+#define StrFormat trio_sprintf
+#define StrFormatMax trio_snprintf
+#define StrFormatAlloc trio_aprintf
+#define StrFormatAppendMax trio_snprintfcat
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* TRIO_TRIO_H */
diff --git a/org.glite.lb.common/interface/ulm_parse.h b/org.glite.lb.common/interface/ulm_parse.h
new file mode 100644 (file)
index 0000000..ee7d85c
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_ULM_PARSE_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_ULM_PARSE_H__
+
+#ident "$Header$"
+
+#include <sys/time.h>          /* for ULCconvertDate */
+#include <time.h>
+
+/*========= DATA =====================================================*/
+
+#define ULM_DATE_STRING_LENGTH 21
+#define ULM_FIELDS_MAX         100 /* max number of fields */
+#define ULM_PARSE_OK           0
+#define ULM_PARSE_ERROR                -1
+
+#define ULM_EQ '='
+#define ULM_QM '"'
+#define ULM_BS '\\'
+#define ULM_SP ' '
+#define ULM_TB '\t'
+#define ULM_LF '\n'
+
+typedef char *LogLine;
+
+typedef struct _edg_wll_ULMFields {
+  LogLine raw;
+  int    *names;
+  int    *vals;
+  int     num;
+} edg_wll_ULMFields , *p_edg_wll_ULMFields;
+
+/*========= FUNCTIONS ================================================*/
+
+#define EDG_WLL_ULM_CLEAR_FIELDS(x) { if (x) { if (x->vals) free(x->vals); if (x->names) free(x->names); x->num=0; } }
+
+extern p_edg_wll_ULMFields edg_wll_ULMNewParseTable( LogLine );
+extern void    edg_wll_ULMFreeParseTable(p_edg_wll_ULMFields );
+
+extern int     edg_wll_ULMProcessParseTable( p_edg_wll_ULMFields );
+extern char *  edg_wll_ULMGetNameAt( p_edg_wll_ULMFields, int );
+extern char *  edg_wll_ULMGetValueAt ( p_edg_wll_ULMFields, int );
+
+extern double  edg_wll_ULMDateToDouble( const char *s );
+void           edg_wll_ULMDateToTimeval( const char *s, struct timeval *tv );
+extern int     edg_wll_ULMTimevalToDate( long sec, long usec, char *dstr );
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_ULM_PARSE_H__ */
diff --git a/org.glite.lb.common/interface/xml_conversions.h b/org.glite.lb.common/interface/xml_conversions.h
new file mode 100644 (file)
index 0000000..bb1d307
--- /dev/null
@@ -0,0 +1,132 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_XML_CONVERSIONS_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_XML_CONVERSIONS_H__
+
+#ident "$Header$"
+
+#include "glite/lb/events.h"
+#include "glite/lb/consumer.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/load.h"
+#include "glite/lb/notification.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EDG_WLL_STAT_NO_JOBS    1024   /* private flags for edg_wll_QueryJobs */
+#define EDG_WLL_STAT_NO_STATES  2048
+
+enum edg_wll_QueryType {
+       EDG_WLL_QUERY_TYPE_UNDEF,
+       EDG_WLL_QUERY_TYPE_JOB_CONDITION,
+       EDG_WLL_QUERY_TYPE_EVENT_CONDITION,
+};     
+
+typedef struct _edg_wll_XML_ctx {
+       edg_wll_Context ctx;                    
+        XML_Parser      p;
+       char            *message_body;          /* copy of pointer to data to be parsed */              
+        edg_wll_EventCode   eventCode;          /* code of processed event */
+        int             position;               /* row index in the result list */
+                                                /* always should mean 'where to write' */
+       int             position2;              /* used only for event_conditions */
+       int             max_index;              /* used in IntList */
+       int             row;                    /* row index in QueryRec job_conditions */
+       int             row2;                   /* row index in QueryRec event_conditions */
+        int             level;                  /* level of XML tags nesting */
+        char            element[50];            /* name of actual XML tag */
+        char            *char_buf;              /* for storing 'between tags' data */
+        int             char_buf_len;
+       char            *XML_tag;               /* for handling *ListOut's tag comparisons */
+       char            *XML_tag2;
+       edg_wll_QueryRec        **job_conditions;       /* temporal results */
+       edg_wll_QueryRec        **event_conditions;
+       enum edg_wll_QueryType  type;   
+       int                     flags;
+        edg_wlc_JobId          *jobsOutGlobal;   
+        edg_wll_Event          *eventsOutGlobal;
+        edg_wll_JobStat        *jobStatGlobal;
+        edg_wll_JobStat        jobStatSingleGlobal;
+       char                    **strListGlobal;
+       int                     *intListGlobal;
+       int                     (*tagToIndex)();
+       char                    *(*indexToTag)();
+       edg_wll_TagValue        *tagListGlobal;
+       edg_wll_JobStat         *stsListGlobal;
+       edg_wll_PurgeRequest    purgeRequestGlobal;
+       edg_wll_PurgeResult     purgeResultGlobal;
+       edg_wll_DumpRequest     dumpRequestGlobal;
+       edg_wll_DumpResult      dumpResultGlobal;
+       edg_wll_LoadRequest     loadRequestGlobal;
+       edg_wll_LoadResult      loadResultGlobal;
+       edg_wll_QueryRec        **attrsGlobal;
+       char                    *notifFunction;
+       char                    *notifClientAddress;
+       edg_wll_NotifId         notifId;
+       edg_wll_NotifChangeOp   notifChangeOp;
+       time_t                  notifValidity;
+       int                     errCode;
+       int                     bound;          /* marks 2nd value of within operator */
+       char                    *errDesc;
+       long                    stat_begin;     /* begin of jobStat tag */
+       long                    jobQueryRec_begin;      /* begin of orJobConditions  tag */
+        char            *errtxt;                /* variable for cummulating error messages   */
+       char            *warntxt;               /* variable for cummulating warning messages */
+} edg_wll_XML_ctx;
+
+void edg_wll_initXMLCtx(edg_wll_XML_ctx *c);
+void edg_wll_freeXMLCtx(edg_wll_XML_ctx *c);
+void edg_wll_freeBuf(edg_wll_XML_ctx *c);
+
+
+#define edg_wll_add_bool_to_XMLBody edg_wll_add_int_to_XMLBody
+#define edg_wll_add_port_to_XMLBody edg_wll_add_uint16_t_to_XMLBody
+#define edg_wll_from_string_to_bool edg_wll_from_string_to_uint16_t
+#define edg_wll_from_string_to_port edg_wll_from_string_to_int
+
+void edg_wll_add_string_to_XMLBody(char **body, const char *toAdd, const char *tag, const char *null);
+void edg_wll_add_tagged_string_to_XMLBody(char **body, const char *toAdd, const char *tag, const char *name, const char *tag2, const char *null);
+void edg_wll_add_int_to_XMLBody(char **body, const int toAdd, const char *tag, const int null);
+void edg_wll_add_timeval_to_XMLBody(char **body, struct timeval toAdd, const char *tag, const struct timeval null);
+void edg_wll_add_jobid_to_XMLBody(char **body, edg_wlc_JobId toAdd, const char *tag, const void *null);
+void edg_wll_add_notifid_to_XMLBody(char **body, edg_wll_NotifId toAdd, const char *tag, const void *null);
+void edg_wll_add_edg_wll_JobStatCode_to_XMLBody(char **body, edg_wll_JobStatCode toAdd, const char *tag, const edg_wll_JobStatCode null);
+void edg_wll_add_edg_wll_EventCode_to_XMLBody(char **body, edg_wll_EventCode toAdd, const char *tag, const edg_wll_EventCode null);
+void edg_wll_add_time_t_to_XMLBody(char **body, const time_t toAdd, const char *tag, const time_t null);
+void edg_wll_add_tagged_time_t_to_XMLBody(char **body, const time_t toAdd, const char *tag, const char *name, const char *tag2, const time_t null);
+void edg_wll_add_uint16_t_to_XMLBody(char **body, const uint16_t toAdd, const char *tag, const uint16_t null);
+void edg_wll_add_logsrc_to_XMLBody(char **body, const edg_wll_Source toAdd, const char *tag, const edg_wll_Source null);
+void edg_wll_add_intlist_to_XMLBody(char **body, const int *toAdd, const char *tag, char *(*indexToTag)(), const char *indent, const int from, const int to);
+void edg_wll_add_strlist_to_XMLBody(char **body, char * const *toAdd, const char *tag, const char *subTag, const char *indent, const  char *null);
+void edg_wll_add_taglist_to_XMLBody(char **body,  const edg_wll_TagValue *toAdd, const char *tag,  const char *subTag, const char *indent, const char *subTag2, const  char *null);
+void edg_wll_add_time_t_list_to_XMLBody(char **body, const time_t *toAdd, const char *tag, char *(*indexToTag)(), const char *indent, const int from, const int to);
+char *edg_wll_from_string_to_string(edg_wll_XML_ctx *XMLCtx);
+edg_wlc_JobId edg_wll_from_string_to_jobid(edg_wll_XML_ctx *XMLCtx);
+edg_wll_NotifId edg_wll_from_string_to_notifid(edg_wll_XML_ctx *XMLCtx);
+edg_wll_JobStatCode edg_wll_from_string_to_edg_wll_JobStatCode(edg_wll_XML_ctx *XMLCtx);
+int edg_wll_from_string_to_int(edg_wll_XML_ctx *XMLCtx);
+long edg_wll_from_string_to_long(edg_wll_XML_ctx *XMLCtx);
+uint16_t edg_wll_from_string_to_uint16_t(edg_wll_XML_ctx *XMLCtx);
+struct timeval edg_wll_from_string_to_timeval(edg_wll_XML_ctx *XMLCtx);
+time_t edg_wll_from_string_to_time_t(edg_wll_XML_ctx *XMLCtx);
+edg_wll_Source edg_wll_from_string_to_logsrc(edg_wll_XML_ctx *XMLCtx);
+
+char *edg_wll_stat_flags_to_string(int flags);
+int edg_wll_string_to_stat_flags(char *cflags);
+char *edg_wll_purge_flags_to_string(int flags);
+int edg_wll_string_to_purge_flags(char *cflags);
+int edg_wll_StringToDumpConst(const char *name);
+char *edg_wll_DumpConstToString(int dumpConst);
+int edg_wll_StringTodone_code(const char *name);
+char *edg_wll_done_codeToString(int done_codeConst);
+edg_wll_QueryAttr edg_wll_StringToquery_attr(const char *name);
+char *edg_wll_query_attrToString(edg_wll_QueryAttr query_attrConst);
+edg_wll_NotifChangeOp edg_wll_StringToNotifChangeOp(const char *name);
+char *edg_wll_NotifChangeOpToString(edg_wll_NotifChangeOp notifChangeOpConst);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_XML_CONVERSIONS_H__ */
diff --git a/org.glite.lb.common/interface/xml_parse.h b/org.glite.lb.common/interface/xml_parse.h
new file mode 100644 (file)
index 0000000..b87d806
--- /dev/null
@@ -0,0 +1,58 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_XML_PARSE_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_XML_PARSE_H__
+
+#ident "$Header$"
+
+
+#include "glite/lb/purge.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/load.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/notification.h"
+
+/* function for parsing XML responses from server */
+
+extern edg_wll_ErrorCode edg_wll_ParseQueryEvents(edg_wll_Context, char *, edg_wll_Event **);
+
+extern edg_wll_ErrorCode edg_wll_ParseQueryJobs(edg_wll_Context, char *, edg_wlc_JobId **, edg_wll_JobStat **);
+
+extern edg_wll_ErrorCode edg_wll_ParseUserJobs(edg_wll_Context, char *, edg_wlc_JobId **); 
+
+extern edg_wll_ErrorCode edg_wll_ParseJobStat(edg_wll_Context ctx, char *messageBody, long len, edg_wll_JobStat *stat);
+
+extern edg_wll_ErrorCode edg_wll_ParseStrList(edg_wll_Context ctx, char *messageBody, long len, char *tag, char *tag2,  char ***strListOut);
+
+extern edg_wll_ErrorCode edg_wll_ParseIntList(edg_wll_Context ctx, char *messageBody, long len, char *tag, int (*tagToIndex)(),  int **intListOut);
+
+extern edg_wll_ErrorCode edg_wll_ParseTagList(edg_wll_Context ctx, char *messageBody, long len, char *tag, char *tag2,  edg_wll_TagValue **tagListOut);
+
+extern edg_wll_ErrorCode edg_wll_ParseStsList(edg_wll_Context ctx, char *messageBody, long len, char *tag, char *tag2,  edg_wll_JobStat **stsListOut);
+
+extern edg_wll_ErrorCode edg_wll_ParsePurgeResult(edg_wll_Context ctx, char *messageBody, edg_wll_PurgeResult *result);
+
+extern edg_wll_ErrorCode edg_wll_ParseDumpResult(edg_wll_Context ctx, char *messageBody, edg_wll_DumpResult *result);
+
+extern edg_wll_ErrorCode edg_wll_ParseLoadResult(edg_wll_Context ctx, char *messageBody, edg_wll_LoadResult *result);
+
+extern edg_wll_ErrorCode edg_wll_ParseIndexedAttrs(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***attrs);
+
+extern edg_wll_ErrorCode edg_wll_ParseNotifResult(edg_wll_Context ctx, char *messageBody, time_t *validity);
+
+extern int edg_wll_QueryEventsRequestToXML(edg_wll_Context ctx, const edg_wll_QueryRec **job_conditions, const edg_wll_QueryRec **event_conditions, char **send_mess);
+
+extern int edg_wll_JobQueryRecToXML(edg_wll_Context ctx, edg_wll_QueryRec const * const *conditions, char **send_mess);
+
+extern int edg_wll_QueryJobsRequestToXML(edg_wll_Context ctx, const edg_wll_QueryRec **conditions, int flags, char **send_mess);
+
+extern int edg_wll_PurgeRequestToXML(edg_wll_Context ctx, const edg_wll_PurgeRequest *request, char **message);
+
+extern int edg_wll_DumpRequestToXML(edg_wll_Context ctx, const edg_wll_DumpRequest *request, char **message);
+
+extern int edg_wll_LoadRequestToXML(edg_wll_Context ctx, const edg_wll_LoadRequest *request, char **message);
+
+extern int edg_wll_IndexedAttrsRequestToXML(edg_wll_Context ctx, char **message);
+
+extern int edg_wll_NotifRequestToXML( edg_wll_Context ctx, const char *function, const edg_wll_NotifId notifId, const char *address, edg_wll_NotifChangeOp op, edg_wll_QueryRec const * const *conditions, char **message);
+
+       
+#endif /* __EDG_WORKLOAD_LOGGING_COMMON_XML_PARSE_H__ */
diff --git a/org.glite.lb.common/project/properties.xml b/org.glite.lb.common/project/properties.xml
new file mode 100755 (executable)
index 0000000..27df03d
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB Common component common properties">
+
+       <property file="build.properties" />    
+       <property name="subsystem.name" value="${lb.subsystem.name}"/>
+       <property name="subsystem.prefix" value="${lb.subsystem.prefix}"/>
+       <property name="component.prefix" value="common" />
+
+       <import file="${component.general.properties.file}" />
+
+</project>
diff --git a/org.glite.lb.common/src/context.c b/org.glite.lb.common/src/context.c
new file mode 100644 (file)
index 0000000..407b852
--- /dev/null
@@ -0,0 +1,419 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include <globus_config.h>
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include "glite/wms/jobid/strmd5.h"
+#include "glite/wms/jobid/cjobid.h"
+#include "context-int.h"
+#include "glite/lb/producer.h"
+
+static void free_voms_groups(edg_wll_VomsGroups *);
+
+int edg_wll_InitContext(edg_wll_Context *ctx)
+{
+       int i;
+       edg_wll_Context out     = (edg_wll_Context) malloc(sizeof(*out));
+
+       if (!out) return ENOMEM;
+       memset(out,0,sizeof(*out));
+       assert(out->errDesc == NULL);
+
+       out->allowAnonymous = 1;
+       out->notifSock  = -1;
+
+       /* XXX */
+       for (i=0; i<EDG_WLL_PARAM__LAST; i++) edg_wll_SetParam(out,i,NULL);
+
+       out->connPool = (edg_wll_ConnPool *) calloc(out->poolSize, sizeof(edg_wll_ConnPool));
+
+       *ctx = out;
+       return 0;
+}
+
+void edg_wll_FreeContext(edg_wll_Context ctx)
+{
+       struct timeval close_timeout = {0, 50000};
+
+       if (!ctx) return;
+
+       if (ctx->errDesc) free(ctx->errDesc);
+       if (ctx->connPool) {
+               int i;
+               
+               for (i=0; i<ctx->poolSize; i++) {
+                       if (ctx->connPool[i].peerName) free(ctx->connPool[i].peerName);
+                       if (ctx->connPool[i].ssl) 
+                               edg_wll_ssl_close_timeout(ctx->connPool[i].ssl,&close_timeout);
+                       if (ctx->connPool[i].gsiCred) edg_wll_ssl_free(ctx->connPool[i].gsiCred);
+                       if (ctx->connPool[i].buf) free(ctx->connPool[i].buf);
+               }       
+               free(ctx->connPool);
+       }
+       if (ctx->notifSock >=0) close(ctx->notifSock);
+       if (ctx->srvName) free(ctx->srvName);
+       if (ctx->peerName) free(ctx->peerName);
+       if (ctx->vomsGroups.len) free_voms_groups(&ctx->vomsGroups);
+       if (ctx->dumpStorage) free(ctx->dumpStorage);
+       if (ctx->purgeStorage) free(ctx->purgeStorage);
+
+        if (ctx->p_jobid) edg_wlc_JobIdFree(ctx->p_jobid);
+        if (ctx->p_host) free(ctx->p_host);
+        if (ctx->p_instance) free(ctx->p_instance);
+        if (ctx->p_destination) free(ctx->p_destination);
+        if (ctx->p_query_server) free(ctx->p_query_server);
+        if (ctx->p_notif_server) free(ctx->p_notif_server);
+        if (ctx->p_proxy_filename) free(ctx->p_proxy_filename);
+        if (ctx->p_cert_filename) free(ctx->p_cert_filename);
+        if (ctx->p_key_filename) free(ctx->p_key_filename);
+
+       /* do not free (references only)
+        * ctx->job_index
+        * ctx->job_index_cols
+        * ctx->mysql */
+
+       free(ctx);
+}
+
+static const char* const errTexts[] = {
+       /* standard error strings should appear here */
+       "Broken ULM",
+       "Undefined event",
+       "Message incomplete",
+       "Duplicate ULM key",
+       "Misuse of ULM key",
+       "Warning: extra ULM fields",
+       "XML Parse error",
+       "Server response error",
+       "Bad JobId format",
+       "Database call failed",
+       "Bad URL format",
+       "MD5 key clash",
+       "SSL Error",
+       "DNS resolver error",
+       "No JobId specified in context",
+       "No indexed condition in query",
+       "Interlogger protocol error",
+       "Interlogger internal error",
+       "Interlogger has events pending"
+};
+
+const char *edg_wll_GetErrorText(int code) {
+       return code ? 
+               (code <= EDG_WLL_ERROR_BASE ?
+                       strerror(code) :
+                       errTexts[code - EDG_WLL_ERROR_BASE - 1]
+               ) :
+               NULL;
+}
+
+int edg_wll_Error(edg_wll_Context ctx, char **errText, char **errDesc)
+{
+       char    *text = NULL,*desc = NULL;
+       const char* et = edg_wll_GetErrorText(ctx->errCode);
+
+       if (et) {
+               text = strdup(et);
+               if (ctx->errDesc) desc = (char *) strdup(ctx->errDesc);
+       }
+
+       if (errText) *errText = text; else free(text);
+       if (errDesc) *errDesc = desc; else free(desc);
+       return ctx->errCode;
+}
+
+int edg_wll_ResetError(edg_wll_Context ctx)
+{
+       if (ctx->errDesc) free(ctx->errDesc);
+       ctx->errDesc = NULL;
+       ctx->errCode =  0;
+
+       return ctx->errCode;
+}
+
+int edg_wll_SetError(edg_wll_Context ctx,int code,const char *desc)
+{
+       edg_wll_ResetError(ctx);
+       if (code) {
+               ctx->errCode = code;
+               if (desc) ctx->errDesc = (char *) strdup(desc);
+       }
+
+       return ctx->errCode;
+}
+
+
+int edg_wll_UpdateError(edg_wll_Context ctx,int code,const char *desc)
+{
+        char   *new_desc=NULL;
+       char    *old_desc=NULL;
+        const char* err_text = edg_wll_GetErrorText(ctx->errCode);
+
+       /* first fill in the old_desc */
+        if (ctx->errCode) {
+                if (ctx->errDesc) {
+                        if ((err_text)) // && (code) && (code <> ctx->errCode))
+                                asprintf(&old_desc,"%s;; %s",err_text,ctx->errDesc);
+                        else
+                                old_desc = (char *) strdup(ctx->errDesc);
+                } else {
+                        old_desc = (char *) strdup(err_text);
+                }
+       } else {
+                if (ctx->errDesc) old_desc = (char *) strdup(ctx->errDesc);
+        }
+       if (ctx->errDesc) free(ctx->errDesc);
+
+       /* update errDesc */
+        if (old_desc) {
+                if (desc) {
+                        asprintf(&new_desc,"%s;; %s",desc,old_desc);
+                        ctx->errDesc = (char *) strdup(new_desc);
+                } else {
+                        ctx->errDesc = (char *) strdup(old_desc);
+               }
+        } else {
+                if (desc) ctx->errDesc = (char *) strdup(desc);
+        }
+
+       /* update errCode */
+        if (code) {
+                ctx->errCode = code;
+        }
+
+        if (new_desc) free(new_desc);
+        if (old_desc) free(old_desc);
+
+        return ctx->errCode;
+}
+
+static const char* const srcNames[] = {
+       "Undefined",
+       "UserInterface",
+       "NetworkServer",
+       "WorkloadManager",
+       "BigHelper",
+       "JobController",
+       "LogMonitor",
+       "LRMS",
+       "Application",
+};
+
+edg_wll_Source edg_wll_StringToSource(const char *name)
+{
+       int     i;
+/* XXX: remove
+       for (i=1; srcNames[i] && strcmp(name,srcNames[i]); i++);
+       return srcNames[i] ? i : EDG_WLL_SOURCE_NONE;
+*/
+        for (i=1; i<sizeof(srcNames)/sizeof(srcNames[0]); i++)
+                if (strcasecmp(srcNames[i],name) == 0) return (edg_wll_Source) i;
+        return EDG_WLL_SOURCE_NONE;
+}
+
+char * edg_wll_SourceToString(edg_wll_Source src)
+{
+        if (src < EDG_WLL_SOURCE_NONE || src >= EDG_WLL_SOURCE__LAST) return NULL;
+        return strdup(srcNames[src]);
+}
+
+static const char* const queryResultNames[] = {
+       "Undefined",
+       "None",
+       "All",
+       "Limited"
+};
+
+edg_wll_QueryResults edg_wll_StringToQResult(const char *name)
+{
+       int     i;
+
+       for (i=1; i<sizeof(queryResultNames)/sizeof(queryResultNames[0]); i++)
+               if (strcasecmp(queryResultNames[i],name) == 0)
+                       return (edg_wll_QueryResults) i;
+
+       return EDG_WLL_QUERYRES_UNDEF;
+}
+
+char * edg_wll_QResultToString(edg_wll_QueryResults res)
+{
+       if (res < EDG_WLL_QUERYRES_NONE || res >= EDG_WLL_QUERYRES__LAST)
+               return NULL;
+
+       return strdup(queryResultNames[res]);
+}
+
+
+char *edg_wll_GetSequenceCode(const edg_wll_Context ctx)
+{
+       unsigned int *c;
+       char *ret = NULL;
+
+       c = &ctx->p_seqcode.c[0];
+       asprintf(&ret, "UI=%06d:NS=%010d:WM=%06d:BH=%010d:JSS=%06d"
+                               ":LM=%06d:LRMS=%06d:APP=%06d",
+                       c[EDG_WLL_SOURCE_USER_INTERFACE],
+                       c[EDG_WLL_SOURCE_NETWORK_SERVER],
+                       c[EDG_WLL_SOURCE_WORKLOAD_MANAGER],
+                       c[EDG_WLL_SOURCE_BIG_HELPER],
+                       c[EDG_WLL_SOURCE_JOB_SUBMISSION],
+                       c[EDG_WLL_SOURCE_LOG_MONITOR],
+                       c[EDG_WLL_SOURCE_LRMS],
+                       c[EDG_WLL_SOURCE_APPLICATION]);
+       return ret;
+}
+
+int edg_wll_SetSequenceCode(edg_wll_Context ctx,
+                       const char * seqcode_str, int seq_type)
+{
+       int res;
+       unsigned int *c;
+       int duplicate = 0;
+
+       edg_wll_ResetError(ctx);
+
+       if (seq_type == EDG_WLL_SEQ_DUPLICATE) {
+               duplicate = 1;
+       } else if (seq_type != EDG_WLL_SEQ_NORMAL)
+               return edg_wll_SetError(ctx, EINVAL,
+                       "edg_wll_SetSequenceCode(): unrecognized value of seq_type parameter");
+       
+       if (!seqcode_str) {
+               memset(&ctx->p_seqcode,0,sizeof ctx->p_seqcode);
+               return 0;
+       }
+
+       c = &ctx->p_seqcode.c[0];
+       res =  sscanf(seqcode_str, "UI=%d:NS=%d:WM=%d:BH=%d:JSS=%d:LM=%d:LRMS=%d:APP=%d",
+                       &c[EDG_WLL_SOURCE_USER_INTERFACE],
+                       &c[EDG_WLL_SOURCE_NETWORK_SERVER],
+                       &c[EDG_WLL_SOURCE_WORKLOAD_MANAGER],
+                       &c[EDG_WLL_SOURCE_BIG_HELPER],
+                       &c[EDG_WLL_SOURCE_JOB_SUBMISSION],
+                       &c[EDG_WLL_SOURCE_LOG_MONITOR],
+                       &c[EDG_WLL_SOURCE_LRMS],
+                       &c[EDG_WLL_SOURCE_APPLICATION]);
+
+       assert(EDG_WLL_SOURCE__LAST == 9);
+       if (res != EDG_WLL_SOURCE__LAST-1)
+               return edg_wll_SetError(ctx, EINVAL, "edg_wll_SetSequenceCode(): syntax error in sequence code");
+
+       if (duplicate) {
+               if (ctx->p_source <= EDG_WLL_SOURCE_NONE || ctx->p_source >= EDG_WLL_SOURCE__LAST)
+                       return edg_wll_SetError(ctx,EINVAL,"edg_wll_SetSequenceCode(): context param: source missing");
+               c[ctx->p_source] = time(NULL);
+       }
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+int edg_wll_IncSequenceCode(edg_wll_Context ctx)
+{
+       edg_wll_ResetError(ctx);
+       
+       if (ctx->p_source <= EDG_WLL_SOURCE_NONE || ctx->p_source >= EDG_WLL_SOURCE__LAST)
+               return edg_wll_SetError(ctx,EINVAL,"edg_wll_IncSequenceCode(): context param: source missing");
+
+       ctx->p_seqcode.c[ctx->p_source]++;
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+int edg_wll_GetLoggingJob(const edg_wll_Context ctx,edg_wlc_JobId *out)
+{
+       return edg_wlc_JobIdDup(ctx->p_jobid,out);
+}
+
+int edg_wll_GenerateSubjobIds(
+       edg_wll_Context ctx,
+       const edg_wlc_JobId     parent,
+       int                     num_subjobs,
+       const char *            seed,
+       edg_wlc_JobId **        subjobs)
+{
+       int subjob, ret;
+       char *p_unique, *p_bkserver, *intseed;
+       char *unhashed, *hashed;
+       unsigned int p_port;
+       edg_wlc_JobId *retjobs;
+
+
+       if (num_subjobs < 1)
+               return edg_wll_SetError(ctx, EINVAL,
+                               "edg_wll_GenerateSubjobIds(): num_subjobs < 1");
+       if (seed == NULL)
+               intseed = edg_wll_GetSequenceCode(ctx);
+       else
+               intseed = strdup(seed);
+
+       p_unique = edg_wlc_JobIdGetUnique(parent);
+       edg_wlc_JobIdGetServerParts(parent, &p_bkserver, &p_port);
+
+       retjobs = calloc(num_subjobs+1, sizeof(edg_wlc_JobId));
+
+       if (p_unique == NULL ||
+               intseed == NULL ||
+               p_bkserver == NULL ||
+               retjobs == NULL)
+               return edg_wll_SetError(ctx, ENOMEM, NULL);
+
+       for (subjob = 0; subjob < num_subjobs; subjob++) {
+
+               asprintf(&unhashed, "%s,%s,%d", p_unique, intseed, subjob);
+               if (unhashed == NULL) {
+                       edg_wll_SetError(ctx, ENOMEM, "edg_wll_GenerateSubjobIds(): asprintf() error");
+                       goto handle_error;
+               }
+
+               hashed = str2md5base64(unhashed);
+               free(unhashed);
+               if (hashed == NULL) {
+                       edg_wll_SetError(ctx, ENOMEM, "edg_wll_GenerateSubjobIds(): str2md5base64() error");
+                       goto handle_error;
+               }
+
+               ret = edg_wlc_JobIdRecreate(p_bkserver, p_port, hashed, &retjobs[subjob]);
+               free(hashed);
+               if (ret != 0) {
+                       edg_wll_SetError(ctx, ret, "edg_wll_GenerateSubjobIds(): edg_wlc_JobIdRecreate() error");
+                       goto handle_error;
+               }
+       }
+       
+       free(intseed);
+       free(p_unique);
+       free(p_bkserver);
+
+       *subjobs = retjobs;
+       return 0;
+
+    handle_error:
+       free(intseed);
+       free(p_unique);
+       free(p_bkserver);
+       for ( subjob-- ;subjob >= 0; subjob--)
+               edg_wlc_JobIdFree(retjobs[subjob]);
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+static void
+free_voms_groups(edg_wll_VomsGroups *groups)
+{
+   size_t len;
+
+   if (groups == NULL)
+      return;
+
+   for (len = 0; len < groups->len; len++) {
+      if (groups->val[len].vo)
+        free(groups->val[len].vo);
+      if (groups->val[len].name)
+        free(groups->val[len].name);
+   }
+}
diff --git a/org.glite.lb.common/src/dgssl.c b/org.glite.lb.common/src/dgssl.c
new file mode 100644 (file)
index 0000000..1e109e7
--- /dev/null
@@ -0,0 +1,679 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <ares.h>
+
+#include "globus_config.h"
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include "dgssl.h"
+
+/* This two functions are not in globus sslutils.h */
+void proxy_verify_ctx_release(proxy_verify_ctx_desc *pvxd);
+void proxy_verify_ctx_init(proxy_verify_ctx_desc *pvxd);
+
+#include "globus_gss_assist.h"
+
+#define tv_sub(a,b) {\
+       (a).tv_usec -= (b).tv_usec;\
+       (a).tv_sec -= (b).tv_sec;\
+       if ((a).tv_usec < 0) {\
+               (a).tv_sec--;\
+               (a).tv_usec += 1000000;\
+       }\
+}
+
+static int decrement_timeout(struct timeval *timeout, struct timeval before, struct timeval after)
+{
+        (*timeout).tv_sec = (*timeout).tv_sec - (after.tv_sec - before.tv_sec);
+        (*timeout).tv_usec = (*timeout).tv_usec - (after.tv_usec - before.tv_usec);
+        while ( (*timeout).tv_usec < 0) {
+                (*timeout).tv_sec--;
+                (*timeout).tv_usec += 1000000;
+        }
+        if ( ((*timeout).tv_sec < 0) || (((*timeout).tv_sec == 0) && ((*timeout).tv_usec == 0)) ) return(1);
+        else return(0);
+}
+
+
+static int handle_ssl_error(int sock,int err,struct timeval *to);
+
+static unsigned char dh512_p[]={
+        0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
+        0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
+        0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
+        0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
+        0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
+        0x47,0x74,0xE8,0x33,
+        };
+static unsigned char dh512_g[]={
+        0x02,
+        };
+
+static DH *get_dh512(void)
+        {
+        DH *dh=NULL;
+
+        if ((dh=DH_new()) == NULL) return(NULL);
+        dh->p=BN_bin2bn(dh512_p,sizeof(dh512_p),NULL);
+        dh->g=BN_bin2bn(dh512_g,sizeof(dh512_g),NULL);
+        if ((dh->p == NULL) || (dh->g == NULL))
+                return(NULL);
+        return(dh);
+        }
+
+struct asyn_result {
+       struct hostent *ent;
+       int             err;
+};
+
+/* ares callback handler for ares_gethostbyname()       */
+static void callback_handler(void *arg, int status, struct hostent *h) {
+       struct asyn_result *arp = (struct asyn_result *) arg;
+
+       switch (status) {
+          case ARES_SUCCESS:
+               if (h && h->h_addr_list[0]) {
+                       arp->ent->h_addr_list =
+                               (char **) malloc(2 * sizeof(char *));
+                       if (arp->ent->h_addr_list == NULL) {
+                               arp->err = NETDB_INTERNAL;
+                               break;
+                       }
+                       arp->ent->h_addr_list[0] =
+                               malloc(sizeof(struct in_addr));
+                       if (arp->ent->h_addr_list[0] == NULL) {
+                               free(arp->ent->h_addr_list);
+                               arp->err = NETDB_INTERNAL;
+                               break;
+                       }
+                       memcpy(arp->ent->h_addr_list[0], h->h_addr_list[0],
+                               sizeof(struct in_addr));
+                       arp->ent->h_addr_list[1] = NULL;
+                       arp->err = NETDB_SUCCESS;
+               } else {
+                       arp->err = NO_DATA;
+               }
+               break;
+           case ARES_EBADNAME:
+           case ARES_ENOTFOUND:
+               arp->err = HOST_NOT_FOUND;
+               break;
+           case ARES_ENOTIMP:
+               arp->err = NO_RECOVERY;
+               break;
+           case ARES_ENOMEM:
+           case ARES_EDESTRUCTION:
+           default:
+               arp->err = NETDB_INTERNAL;
+               break;
+       }
+}
+
+
+static void free_hostent(struct hostent *h){
+        int i;
+
+        if (h) {
+                if (h->h_name) free(h->h_name);
+               if (h->h_aliases) {
+                       for (i=0; h->h_aliases[i]; i++) free(h->h_aliases[i]);
+                       free(h->h_aliases);
+               }
+                if (h->h_addr_list) {
+                        for (i=0; h->h_addr_list[i]; i++) free(h->h_addr_list[i]);
+                        free(h->h_addr_list);
+                }
+                free(h);
+        }
+}
+
+static int asyn_gethostbyname(char **addrOut, char const *name, struct timeval *timeout) {
+       struct asyn_result ar;
+       ares_channel channel;
+       int nfds;
+       fd_set readers, writers;
+       struct timeval tv, *tvp;
+       struct timeval start_time,check_time;
+
+
+/* start timer */
+       gettimeofday(&start_time,0);
+
+/* ares init */
+       if ( ares_init(&channel) != ARES_SUCCESS ) return(NETDB_INTERNAL);
+       ar.ent = (struct hostent *) calloc (sizeof(*ar.ent),1);
+
+/* query DNS server asynchronously */
+       ares_gethostbyname(channel, name, AF_INET, callback_handler,
+                               (void *) &ar);
+
+/* wait for result */
+       while (1) {
+               FD_ZERO(&readers);
+               FD_ZERO(&writers);
+               nfds = ares_fds(channel, &readers, &writers);
+               if (nfds == 0)
+                       break;
+
+               gettimeofday(&check_time,0);
+               if (decrement_timeout(timeout, start_time, check_time)) {
+                       ares_destroy(channel);
+                       free_hostent(ar.ent);
+                       return(TRY_AGAIN);
+               }
+               start_time = check_time;
+
+               tvp = ares_timeout(channel, timeout, &tv);
+
+               switch ( select(nfds, &readers, &writers, NULL, tvp) ) {
+                       case -1: if (errno != EINTR) {
+                                       ares_destroy(channel);
+                                       free_hostent(ar.ent);
+                                       return NETDB_INTERNAL;
+                                } else
+                                       continue;
+                       case 0: 
+                               FD_ZERO(&readers);
+                               FD_ZERO(&writers);
+                               /* fallthrough */
+                       default: ares_process(channel, &readers, &writers);
+               }
+       }
+
+
+       ares_destroy(channel);
+
+       if (ar.err == NETDB_SUCCESS) {
+               *addrOut = malloc(sizeof(struct in_addr));
+               memcpy(*addrOut,ar.ent->h_addr_list[0], sizeof(struct in_addr));
+               free_hostent(ar.ent);
+       }
+       return(ar.err);
+}
+
+
+
+void edg_wll_ssl_set_noauth(proxy_cred_desc* cred_handle)
+{ DH *dh=NULL;
+
+  SSL_CTX_set_cipher_list(cred_handle->gs_ctx, 
+     "ADH:RSA:HIGH:MEDIUM:LOW:EXP:+eNULL:+aNULL");
+  dh=get_dh512();
+  SSL_CTX_set_tmp_dh(cred_handle->gs_ctx,dh);
+  DH_free(dh);
+}
+
+proxy_cred_desc *edg_wll_ssl_init(int verify, int callback, char *p_cert_file,char *p_key_file,
+                  int ask_passwd, int noauth)
+{
+  proxy_cred_desc * cred_handle = NULL;
+  char *certdir=NULL;
+  int (*pw_cb)() = NULL;
+  int load_err = 0;
+
+  if (ask_passwd == 0)
+     pw_cb = proxy_password_callback_no_prompt;
+
+  cred_handle = proxy_cred_desc_new();
+  proxy_get_filenames(cred_handle,1,NULL,&certdir,NULL,NULL,NULL);
+  if (!noauth) {
+     if ((p_cert_file!=NULL) && (p_key_file!=NULL)) {
+       load_err = proxy_load_user_cert(cred_handle,p_cert_file,NULL,NULL);
+       if (load_err == 0)
+          load_err = proxy_load_user_key(cred_handle,p_key_file,pw_cb,NULL);
+        if (load_err == 0) {
+             if (proxy_check_proxy_name(cred_handle->ucert)>0) {
+                  cred_handle->type =CRED_TYPE_PROXY;
+                if (cred_handle->cert_chain == NULL)
+                     cred_handle->cert_chain = sk_X509_new_null();
+                proxy_load_user_proxy(cred_handle->cert_chain,p_cert_file,NULL);
+                /*
+                if (cred_handle->cert_chain)
+                     for (int i=0;i<sk_X509_num(cred_handle->cert_chain);i++) 
+                         X509_STORE_add_cert(cred_handle->gs_ctx->cert_store,
+                                     sk_X509_value(cred_handle->cert_chain,i));
+                */
+             } else cred_handle->type = CRED_TYPE_PERMANENT;
+        } 
+     } 
+     if (load_err == 0)
+       proxy_init_cred(cred_handle,pw_cb,NULL);
+  }
+
+  if (   (cred_handle->gs_ctx != NULL
+      && !SSL_CTX_check_private_key(cred_handle->gs_ctx))
+      || noauth == 1
+      || load_err) {
+    if (cred_handle->ucert != NULL) {
+      X509_free(cred_handle->ucert);
+      cred_handle->ucert = NULL;
+    }
+    if (cred_handle->upkey != NULL) {
+      EVP_PKEY_free(cred_handle->upkey);
+      cred_handle->upkey = NULL;
+    }
+    if (cred_handle->gs_ctx)
+      SSL_CTX_free(cred_handle->gs_ctx);
+    cred_handle->gs_ctx=SSL_CTX_new(SSLv3_method());
+    SSL_CTX_set_options(cred_handle->gs_ctx,0);
+    SSL_CTX_sess_set_cache_size(cred_handle->gs_ctx,5);
+    SSL_CTX_load_verify_locations(cred_handle->gs_ctx,NULL,certdir);
+  }
+
+  if (cred_handle->gs_ctx != NULL) {
+      SSL_CTX_set_verify(cred_handle->gs_ctx,verify,
+            (callback==0)?NULL:proxy_verify_callback);
+      SSL_CTX_set_purpose(cred_handle->gs_ctx,X509_PURPOSE_ANY);
+      SSL_CTX_set_session_id_context(cred_handle->gs_ctx, "DG_BKSERVER",
+           strlen("DG_BKSERVER"));
+      if (noauth == 1)
+        edg_wll_ssl_set_noauth(cred_handle);
+  }
+
+  free(certdir);
+  return(cred_handle);
+}
+
+void edg_wll_ssl_get_my_subject(proxy_cred_desc *cred_handle, char **my_subject_name)
+{
+  if ((my_subject_name!=NULL) && (cred_handle->ucert!=NULL))
+     *my_subject_name=X509_NAME_oneline(X509_get_subject_name(
+          cred_handle->ucert), NULL, 0);
+}
+
+void edg_wll_ssl_get_my_subject_base(proxy_cred_desc *cred_handle, char **my_subject_name)
+{
+  X509_NAME *base;
+  if ((my_subject_name!=NULL) && (cred_handle->ucert!=NULL)) {
+       X509_NAME       *s = X509_get_subject_name(cred_handle->ucert);
+       
+       base = X509_NAME_dup(s);
+       proxy_get_base_name(base);
+       *my_subject_name=strdup(X509_NAME_oneline(base,NULL, 0));
+       X509_NAME_free(base);
+  }
+}
+
+int edg_wll_ssl_free(proxy_cred_desc * cred_handle)
+{ return(proxy_cred_desc_free(cred_handle));
+}
+
+int edg_wll_ssl_connect(proxy_cred_desc *cred_handle,char const *hostname,int port,
+       struct timeval *timeout,SSL **sslp)
+{
+       int     sock,ret;
+       struct sockaddr_in      a;
+       SSL_CTX *ctx;
+       SSL     *ssl;
+       struct timeval  before,after,to;
+       int     sock_err;
+       socklen_t       err_len;
+        char *certdir=NULL, *addr;
+        proxy_verify_ctx_desc verify_ctx_area;
+        proxy_verify_desc verify_area;
+       int     h_errno;
+
+               
+       sock = socket(AF_INET, SOCK_STREAM, 0);
+       if (sock < 0) return EDG_WLL_SSL_ERROR_ERRNO;
+
+       if (timeout) {
+               int     flags = fcntl(sock, F_GETFL, 0);
+               if (fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0)
+                       return EDG_WLL_SSL_ERROR_ERRNO;
+               gettimeofday(&before,NULL);
+       }
+
+       switch (h_errno = asyn_gethostbyname(&addr, hostname, timeout)) {
+               case NETDB_SUCCESS:
+                       memset(&a,0,sizeof a);
+                       a.sin_family = AF_INET;
+                       memcpy(&a.sin_addr.s_addr,addr,sizeof a.sin_addr.s_addr);
+                       a.sin_port = htons(port);
+                       free(addr);
+                       break;
+               case TRY_AGAIN:
+                       close(sock);
+                       return EDG_WLL_SSL_ERROR_TIMEOUT;
+               case NETDB_INTERNAL: 
+                       /* fall through */
+               default:
+                       close(sock);
+                       /* h_errno may be thread safe with Linux pthread libs,
+                        * but such an assumption is not portable
+                        */
+                       errno = h_errno;
+                       return EDG_WLL_SSL_ERROR_HERRNO;
+       }
+
+       if (connect(sock,(struct sockaddr *) &a,sizeof a) < 0) {
+               if (timeout && errno == EINPROGRESS) {
+                       fd_set  fds;
+                       FD_ZERO(&fds);
+                       FD_SET(sock,&fds);
+                       memcpy(&to,timeout,sizeof to);
+                       gettimeofday(&before,NULL);
+                       switch (select(sock+1,NULL,&fds,NULL,&to)) {
+                               case -1: close(sock);
+                                        return EDG_WLL_SSL_ERROR_ERRNO;
+                               case 0: close(sock);
+                                       return EDG_WLL_SSL_ERROR_TIMEOUT;
+                       }
+                       gettimeofday(&after,NULL);
+                       tv_sub(after,before);
+                       tv_sub(*timeout,after);
+
+                       err_len = sizeof sock_err;
+                       if (getsockopt(sock,SOL_SOCKET,SO_ERROR,&sock_err,&err_len)) {
+                               close(sock);
+                               return EDG_WLL_SSL_ERROR_ERRNO;
+                       }
+                       if (sock_err) {
+                               close(sock);
+                               errno = sock_err;
+                               return EDG_WLL_SSL_ERROR_ERRNO;
+                       }
+               }
+               else {
+                       close(sock);
+                       return EDG_WLL_SSL_ERROR_ERRNO;
+               }
+       }
+
+       ctx = cred_handle->gs_ctx;
+       ssl = SSL_new(ctx);
+       if (!ssl) {
+               close(sock);
+               return EDG_WLL_SSL_ERROR_SSL;
+       }
+       SSL_set_ssl_method(ssl,SSLv3_method());
+       SSL_set_fd(ssl, sock);
+        proxy_get_filenames(NULL,1,NULL,&certdir,NULL,NULL,NULL);
+        proxy_verify_ctx_init(&verify_ctx_area);
+        proxy_verify_init(&verify_area,&verify_ctx_area);
+        SSL_set_ex_data(ssl, PVD_SSL_EX_DATA_IDX, (char *)&verify_area);
+        if (certdir!=NULL) verify_ctx_area.certdir=certdir;
+        
+       if (timeout) SSL_set_mode(ssl, SSL_MODE_ENABLE_PARTIAL_WRITE);
+
+       ret = SSL_connect(ssl);
+       while (ret <= 0) {
+               int     err = SSL_get_error(ssl,ret);
+               if ((err = handle_ssl_error(sock,err,timeout))) {
+                        proxy_verify_release(&verify_area);
+                        proxy_verify_ctx_release(&verify_ctx_area);
+                       SSL_free(ssl);
+                       close(sock);
+                       return err;
+               }
+               ret = SSL_connect(ssl);
+       }
+        proxy_verify_release(&verify_area);
+        proxy_verify_ctx_release(&verify_ctx_area);
+       *sslp = ssl;
+       return EDG_WLL_SSL_OK;
+}
+       
+SSL *edg_wll_ssl_accept(proxy_cred_desc *cred_handle,int sock,struct timeval *timeout)
+{ SSL *ssl = NULL;
+  char *certdir=NULL;
+  SSL_CTX *sslContext;
+  int ret;
+
+
+  proxy_verify_ctx_desc verify_ctx_area;
+  proxy_verify_desc verify_area;
+
+  sslContext=cred_handle->gs_ctx;
+
+  ssl = SSL_new(sslContext);
+  if (ssl == NULL) {
+      fprintf(stderr, "SSL_new(): %s\n",
+              ERR_error_string(ERR_get_error(), NULL));
+      return(NULL);
+  }
+
+  SSL_set_ssl_method(ssl,SSLv23_method());
+  SSL_set_options(ssl,SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1);
+
+  proxy_get_filenames(NULL,1,NULL,&certdir,NULL,NULL,NULL);
+  proxy_verify_ctx_init(&verify_ctx_area);
+  proxy_verify_init(&verify_area,&verify_ctx_area);
+  SSL_set_ex_data(ssl, PVD_SSL_EX_DATA_IDX, (char *)&verify_area);
+  if (certdir!=NULL) verify_ctx_area.certdir=certdir;
+
+  SSL_set_accept_state(ssl);
+  SSL_set_fd(ssl, sock);
+
+  ret = SSL_accept(ssl);
+  while (ret <= 0) {
+     int     err = SSL_get_error(ssl,ret);
+     if ((err = handle_ssl_error(sock,err,timeout))) {
+       proxy_verify_release(&verify_area);
+       proxy_verify_ctx_release(&verify_ctx_area);
+       SSL_free(ssl);
+       return (NULL);
+     }
+     ret = SSL_accept(ssl);
+  }
+
+  proxy_verify_release(&verify_area);
+  proxy_verify_ctx_release(&verify_ctx_area);
+
+  return(ssl);
+}
+
+void edg_wll_ssl_reject(proxy_cred_desc *cred_handle,int sock)
+{ SSL *ssl = NULL;
+  SSL_CTX *sslContext;
+
+  sslContext=cred_handle->gs_ctx;
+
+  ssl = SSL_new(sslContext);
+  if (ssl == NULL) {
+      fprintf(stderr, "SSL_new(): %s\n",
+              ERR_error_string(ERR_get_error(), NULL));
+      return;
+  }
+
+  SSL_set_ssl_method(ssl,SSLv23_method());
+  SSL_set_options(ssl,SSL_OP_NO_SSLv2|SSL_OP_NO_TLSv1);
+
+  SSL_set_accept_state(ssl);
+  SSL_set_fd(ssl, sock);
+
+  SSL_shutdown(ssl);
+  SSL_free(ssl);
+
+  return;
+}
+
+int edg_wll_ssl_close_timeout(SSL *ssl, struct timeval *timeout) 
+{
+  int ret, sock;
+  
+  if (!ssl) return(0);
+  sock = SSL_get_fd(ssl);
+  ret = SSL_shutdown(ssl);
+  while (ret <= 0) {
+     int err = SSL_get_error(ssl, ret);
+     if (ret == 0 && err == SSL_ERROR_SYSCALL) {
+       /* translate misleading error returned by SSL_shutdown() to correct one */
+       switch (errno) {
+               case 0: goto closed; break;
+               case EAGAIN: err = SSL_ERROR_WANT_READ; break;
+               default: /* obey err */ break;
+       }
+     }
+     if (handle_ssl_error(sock, err, timeout))
+          break;
+     ret = SSL_shutdown(ssl);
+  }
+
+closed:
+  ret = SSL_clear(ssl);
+  ret = close(sock);
+  SSL_free(ssl);
+
+  return(0);
+}
+
+int edg_wll_ssl_close(SSL *ssl)
+{
+   struct timeval timeout = {120, 0};
+   return edg_wll_ssl_close_timeout(ssl, &timeout);
+}
+
+int edg_wll_ssl_read(SSL *ssl,void *buf,size_t bufsize,struct timeval *timeout)
+{
+       int     len = 0;
+       int     sock = SSL_get_fd(ssl);
+       
+       len = SSL_read(ssl,buf,bufsize);
+       while (len <= 0) {
+               int     err = SSL_get_error(ssl,len);
+               if ((err = handle_ssl_error(sock,err,timeout))) return err;
+               len = SSL_read(ssl,buf,bufsize);
+       }
+       return len;
+}
+
+int edg_wll_ssl_write(SSL *ssl,const void *buf,size_t bufsize,struct timeval *timeout)
+{
+       int     len = 0;
+       int     sock = SSL_get_fd(ssl);
+       
+       len = SSL_write(ssl,buf,bufsize);
+       while (len <= 0) {
+               int     err = SSL_get_error(ssl,len);
+               if ((err = handle_ssl_error(sock,err,timeout))) return err;
+               len = SSL_write(ssl,buf,bufsize);
+       }
+       return len;
+}
+
+int edg_wll_ssl_read_full(SSL *ssl,void *buf,size_t bufsize,struct timeval *timeout,size_t *total)
+{
+       int     len;
+       *total = 0;
+
+       while (*total < bufsize) {
+               len = edg_wll_ssl_read(ssl,buf+*total,bufsize-*total,timeout);
+               if (len < 0) return len;
+               *total += len;
+       }
+       return 0;
+}
+
+int edg_wll_ssl_write_full(SSL *ssl,const void *buf,size_t bufsize,struct timeval *timeout,size_t *total)
+{
+       int     len;
+       *total = 0;
+
+       while (*total < bufsize) {
+               len = edg_wll_ssl_write(ssl,buf+*total,bufsize-*total,timeout);
+               if (len < 0) return len;
+               *total += len;
+       }
+       return 0;
+}
+
+static int handle_ssl_error(int sock,int err,struct timeval *to)
+{
+       struct timeval  timeout,before,after;
+       fd_set  fds;
+       int     ret = EDG_WLL_SSL_OK;
+       int     saved_errno = errno;
+
+       if (to) { 
+           memcpy(&timeout,to,sizeof timeout);
+          gettimeofday(&before,NULL);
+        }
+       switch (err) {
+               case SSL_ERROR_ZERO_RETURN:
+                       ret = EDG_WLL_SSL_ERROR_EOF;
+                       break;
+               case SSL_ERROR_WANT_READ:
+                       FD_ZERO(&fds);
+                       FD_SET(sock,&fds);
+                       switch (select(sock+1,&fds,NULL,NULL,to?&timeout:NULL)){
+                               case 0: ret = EDG_WLL_SSL_ERROR_TIMEOUT;
+                                       break;
+                               case -1: ret = EDG_WLL_SSL_ERROR_ERRNO;
+                                        break;
+                       }
+                       break;
+               case SSL_ERROR_WANT_WRITE:
+                       FD_ZERO(&fds);
+                       FD_SET(sock,&fds);
+                       switch (select(sock+1,NULL,&fds,NULL,to?&timeout:NULL)){
+                               case 0: ret = EDG_WLL_SSL_ERROR_TIMEOUT;
+                                       break;
+                               case -1: ret = EDG_WLL_SSL_ERROR_ERRNO;
+                                        break;
+                       }
+                       break;
+               case SSL_ERROR_SYSCALL:
+                       if (saved_errno == 0) ret = EDG_WLL_SSL_ERROR_EOF;
+                       else ret = EDG_WLL_SSL_ERROR_ERRNO;
+                       break;
+               case SSL_ERROR_SSL:
+                       ret = EDG_WLL_SSL_ERROR_SSL;
+                       break;
+               default:
+                       fprintf(stderr,"unexpected SSL_get_error code %d\n",err);
+                       abort();
+       }
+       if (to) {
+           gettimeofday(&after,NULL);
+          tv_sub(after,before);
+          tv_sub(*to,after);
+          if (to->tv_sec < 0) {
+               to->tv_sec = 0;
+               to->tv_usec = 0;
+          }
+        }
+
+       return ret;
+}
+
+/* XXX: I'm afraid the contents of stuct stat is somewhat OS dependent */
+
+int edg_wll_ssl_watch_creds(
+               const char      *key_file,
+               const char      *cert_file,
+               time_t          *key_mtime,
+               time_t          *cert_mtime)
+{
+       struct stat     kstat,cstat;
+       int     reload = 0;
+
+       if (!key_file || !cert_file) return 0;
+       if (stat(key_file,&kstat) || stat(cert_file,&cstat)) return -1;
+
+       if (!*key_mtime) *key_mtime = kstat.st_mtime;
+       if (!*cert_mtime) *cert_mtime = cstat.st_mtime;
+
+       if (*key_mtime != kstat.st_mtime) {
+               *key_mtime = kstat.st_mtime;
+               reload = 1;
+       }
+
+       if (*cert_mtime != cstat.st_mtime) {
+               *cert_mtime = cstat.st_mtime;
+               reload = 1;
+       }
+
+       return reload;
+}
diff --git a/org.glite.lb.common/src/escape.c b/org.glite.lb.common/src/escape.c
new file mode 100644 (file)
index 0000000..8ace584
--- /dev/null
@@ -0,0 +1,221 @@
+#ident "$Header$"
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ulm_parse.h"
+#include "escape.h"
+
+/*
+ *----------------------------------------------------------------------
+ * 
+ * \fn char *edg_wll_LogEscape(const char *str)
+ * \param str          a string to escape
+ * \return             new (allocated) escaped string
+ * \brief in given string escape all ULM_QM, ULM_BS and ULM_LF by ULM_BS
+ *
+ * Calls: malloc, strlen
+ *
+ * Algorithm: array lookup
+ *             - the new string will be allocated
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *edg_wll_LogEscape(const char *str)
+{
+unsigned int i,j;
+size_t size;
+char *ret;
+
+if (str == NULL) return NULL;
+if ((size = strlen(str)) == 0) return strdup("");
+
+ret = (char*) malloc(1+2*size*sizeof(char));
+
+j = 0;
+for (i=0; i<size; i++) {
+       if ((str[i] != ULM_BS) && (str[i] != ULM_QM) && (str[i] != ULM_LF)) {
+               ret[j] = str[i];
+               j++;
+       }
+       else {
+               ret[j] = ULM_BS;
+               if (str[i] == ULM_LF) {
+                       ret[j+1] = 'n';
+               }
+               else {
+                       ret[j+1] = str[i];
+               }
+               j += 2;
+       }
+} /* for */
+
+ret[j] = 0;
+
+return ret;
+}
+
+/*
+ *----------------------------------------------------------------------
+ * 
+ * \fn char *edg_wll_LogUnescape(const char *str)
+ * \param str          a string to unescape
+ * \return             new (allocated) unescaped string
+ * \brief in given string unescape all escaped ULM_QM, ULM_BS and ULM_LF
+ *
+ * Calls: malloc, strlen
+ *
+ * Algorithm: array lookup
+ *               - the new string will be allocated
+ *
+ *----------------------------------------------------------------------
+ */
+
+char *edg_wll_LogUnescape(const char *str)
+{
+unsigned int i,j;
+size_t size;
+char *ret;
+
+if (str == NULL) return NULL;
+
+size  = strlen(str);
+ret = (char*) malloc(1+size*sizeof(char));
+
+/*
+j = 0;
+for (i=0; i<size; i++) {
+       if ( (str[i] != ULM_BS) || 
+          ((str[i] == ULM_BS) && ((str[i+1] != ULM_BS) && (str[i+1] != ULM_QM) && (str[i+1] != 'n'))) )
+       {
+               if (str[i] == ULM_LF) { ret[j] = 'n'; }
+               else { ret[j] = str[i]; }
+               j++;
+       }
+} 
+*/
+for (i=j=0; i<size; i++,j++)
+       if (str[i] == ULM_BS) switch(str[++i]) {
+               case 'n': ret[j] = ULM_LF; break;
+               default: ret[j] = str[i]; break;
+       } else { ret[j] = str[i]; }
+
+ret[j] = '\0';
+
+return ret;
+}
+
+static const struct {
+       const char c,*e;
+} xml_etab[] = {
+       { '<',"lt" },
+       { '>',"gt" },
+       { '&',"amp" },
+       { '"',"quot" },
+       { '\'',"apos" },
+       { 0, NULL }
+};
+
+#define XML_ESCAPE_SET "<>&\"'"
+
+char *edg_wll_EscapeXML(const char *in)
+{
+        const char* tmp_in;
+       char    *out;
+       int     cnt,i,j,k;
+
+       if (!in) return NULL;
+
+       for (cnt = 0, tmp_in = in; *tmp_in != '\0'; ++tmp_in) {
+               if (strchr(XML_ESCAPE_SET, *tmp_in) ||
+                       (*tmp_in & 0x7f) < 0x20 /* control character */ ||
+                       (*tmp_in == '%')) cnt++;
+        }
+
+       out = malloc(strlen(in)+1+cnt*5);
+
+       for (i=j=0; in[i]; i++) {
+               for (k=0; xml_etab[k].c && xml_etab[k].c != in[i]; k++);
+               if (xml_etab[k].c) {
+                       int     l;
+
+                       out[j++] = '&';
+                       memcpy(out+j,xml_etab[k].e,l=strlen(xml_etab[k].e));
+                       j += l;
+                       out[j++] = ';';
+               } else if ((in[i] & 0x7f) < 0x20 || in[i] == '%') {
+                       sprintf(out+j, "%%%02x", (unsigned char)in[i]);
+                       j+=3;
+               } else {
+                       out[j++] = in[i];
+               }
+       }
+       out[j] = 0;
+       return out;
+}
+
+char *edg_wll_UnescapeXML(const char *in)
+{
+       char    *out;
+       int     i,j,k;
+       char    xtmp[3];
+       unsigned char   origchar;
+
+       if (!in) return NULL;
+       out = malloc(strlen(in)+1);
+
+       for (i=j=0; in[i]; j++) if (in[i] == '&') {
+               char    *s = strchr(in+i,';');
+               if (s) {
+                       int     l = s-in-i+1;
+                       for (k=0; xml_etab[k].c && strncasecmp(in+i+1,xml_etab[k].e,l-2); k++);
+                       if (xml_etab[k].c) {
+                               out[j] = xml_etab[k].c;
+                               i += l;
+                       } else out[j] = in[i++];
+               } else out[j] = in[i++];
+       } else if (in[i] == '%') {
+               if (isxdigit(xtmp[0]=in[i+1]) && isxdigit(xtmp[1]=in[i+2])) {
+                       xtmp[2] = '\0';
+                       origchar = (unsigned char) strtol(xtmp, NULL, 16);
+                       if ((origchar & 0x7f) < 0x20 || origchar == '%') {
+                               out[j] = origchar;
+                               i += 3;
+                       } else out[j] = in[i++];
+               } else out[j] = in[i++];
+       } else {
+               out[j] = in[i++];
+       }
+       out[j] = 0;
+       return out;
+}
+
+char *edg_wll_EscapeSQL(const char *in)
+{
+        const char* tmp_in;
+       char    *out = NULL;
+       int     i,j,cnt;
+
+       if (!in) return NULL;
+
+       for (cnt = 0, tmp_in = in; (tmp_in = strchr(tmp_in,'\'')) != NULL; ++tmp_in) {
+          ++cnt;
+        }
+       for (tmp_in = in; (tmp_in = strchr(tmp_in,'\\')) != NULL; ++tmp_in) {
+          ++cnt;
+        }
+
+       out = malloc(strlen(in)+1+cnt);
+
+       for (i=j=0; in[i]; i++) {
+               if (in[i] == '\\') out[j++] = '\\';
+               if (in[i] == '\'') out[j++] = '\'';
+               out[j++] = in[i];
+       }
+       out[j] = 0;
+
+       return out;
+}
diff --git a/org.glite.lb.common/src/events.c.T b/org.glite.lb.common/src/events.c.T
new file mode 100644 (file)
index 0000000..2897e71
--- /dev/null
@@ -0,0 +1,349 @@
+#ident "$Header$"
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/producer.h"
+#include "ulm_parse.h"
+
+static const struct timeval null_timeval = {0,0};
+
+/**
+ * Predefined event types names
+ */
+static const char *eventNames[] = {
+       "Undefined",
+@@@{
+for my $e (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       gen "\t\"$e\",\n";
+}
+@@@}
+       "SysCmpStat",
+       "SysClStat",
+};
+
+/**
+ * \fn edg_wll_EventCode edg_wll_StringToEvent(char *name)
+ * \param name          a string event name (e.g. "JobTransfer")
+ * \return corresponding numeric code (edg_wll_EventCode)
+ * \brief convert a string event name to the corresponding numeric code  
+ * Calls: strcasecmp
+ * Algorithm: array lookup
+ */
+edg_wll_EventCode edg_wll_StringToEvent(char *name)
+{
+       unsigned int    i;
+
+       for (i=1; i<sizeof(eventNames)/sizeof(eventNames[0]); i++)
+               if (strcasecmp(eventNames[i],name) == 0) return (edg_wll_EventCode) i;
+       return EDG_WLL_EVENT_UNDEF;
+}
+
+/**
+ * \fn char *edg_wll_EventToString(edg_wll_EventCode event)
+ * \param event         an event numeric code (edg_wll_EventCode)
+ * \return corresponding string (e.g. "JobTransfer")
+ * \brief convert an event numeric code to the corresponding string
+ * Calls: strdup
+ */
+char *edg_wll_EventToString(edg_wll_EventCode event)
+{
+       if ((int)event < 0 || event >= sizeof(eventNames)/sizeof(eventNames[0])) return NULL;
+       return strdup(eventNames[event]);
+}
+
+
+/**
+ * Predefined ULM key types names
+ */
+static const char *keyNames[] = {
+       "Undefined",
+       "DG.EVNT",
+@@@{
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       my $fn = getName $f 'ULM';
+       my $fnu = uc $fn;
+       my $c = $f->{comment};
+       if (hasAlias $f 'ULM') {
+               gen "\t\"$fnu\",\n";
+       } else {
+               gen "\t\"DG.$fnu\",\n";
+       }
+}
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fnu = uc $f->{name};
+               gen "\t\"DG.$tu.$fnu\",\n";
+       }
+}
+@@@}
+       "DG.TYPE",
+};
+
+/*
+ * \fn edg_wll_KeyNameCode edg_wll_StringToKeyName(char *name)
+ * \param name         a string ULM key name (e.g. "DG.JOB.TRANSFER.DEST")
+ * \return corresponding numeric code (edg_wll_KeyNameCode)
+ * \brief convert a string ULM key name to the corresponding numeric code
+ * Calls: strcasecmp
+ * Algorithm: array lookup
+ */
+edg_wll_KeyNameCode edg_wll_StringToKeyName(char *name)
+{
+       unsigned int    i;
+
+       for (i=1; i<sizeof(keyNames)/sizeof(keyNames[0]); i++)
+               if (strcasecmp(keyNames[i],name) == 0) return (edg_wll_KeyNameCode) i;
+       return UNDEFINED;
+}
+
+/*
+ * \fn char *edg_wll_KeyNameToString(edg_wll_KeyNameCode key)
+ * \param key          a ULM key name numeric code (edg_wll_KeyNameCode)
+ * \return corresponding string (e.g. "DG.JOB.TRANSFER.DEST")
+ * \brief convert a ULM key name numeric code to the corresponding string
+ * Calls: strdup
+ */
+char *edg_wll_KeyNameToString(edg_wll_KeyNameCode key)
+{
+       if ((int)key < 0 || key >= sizeof(keyNames)/sizeof(keyNames[0])) return NULL;
+       return strdup(keyNames[key]);
+}
+
+
+/**
+ * Predefined _code_ types names and
+ * related StringTo_code and _code_ToString function implementations
+ */
+@@@{
+$indent = "\t";
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       if ($f->{codes}) {
+               my $fn = ucfirst($f->{name});
+               my $fnu = uc $fn;
+               my $c = "${fn}";                # code
+               my $enum = "enum edg_wll\_$c";  # enum name
+               my $char = "edg_wll\_${fn}Names"; # char name
+# static const char:
+               gen qq{
+/**
+ * Predefined code names for $c
+ */
+static const char \*${char}\[\] = \{
+};
+               gen $indent."\"UNDEFINED\",\n";
+               for (@{$f->{codes}}) {
+                       gen $indent."\"$_->{name}\",\n";
+               }
+               gen "}; \n";
+
+# function StringTo:
+               gen qq{
+/**
+ * \\fn $enum edg_wll_StringTo${c}(char *name)
+ * Calls: strcasecmp
+ * Algorithm: array lookup
+ */
+$enum edg_wll_StringTo${c}(char *name) 
+\{
+       unsigned int    i;
+
+       for (i=1; i<sizeof($char)/sizeof(${char}\[0\]); i++) 
+               if (strcasecmp(${char}\[i\],name) == 0) return ($enum) i;
+       return ($enum) EDG_WLL_${fnu}_UNDEFINED;
+\}
+\n};
+
+# function ToString:
+               gen qq{
+/**
+ * \\fn *edg_wll\_${c}ToString($enum code)
+ * Calls: strdup
+ */
+char *edg_wll\_${c}ToString($enum code) 
+\{
+       if ((int)code < 0 || code >= sizeof($char)/sizeof(${char}\[0\])) return NULL;
+       return strdup(${char}\[code\]);
+
+\}
+\n\n};
+       }
+}
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t . '_';
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               if ($f->{codes}) {
+                       my $fn = ucfirst($f->{name});
+                       my $c = "$t${fn}";              # code
+                       my $enum = "enum edg_wll\_$c";  # enum name
+                       my $char = "edg_wll\_$t${fn}Names"; # char name
+# static const char:
+                       gen qq{
+/**
+ * Predefined code names for $c
+ */
+static const char \*${char}\[\] = \{
+};
+                       gen $indent."\"UNDEFINED\",\n";
+                       for (@{$f->{codes}}) {
+                               gen $indent."\"$_->{name}\",\n";
+                       }
+                       gen "}; \n";
+
+# function StringTo:
+                       gen qq{
+/**
+ * \\fn $enum edg_wll_StringTo${c}(char *name)
+ * Calls: strcasecmp
+ * Algorithm: array lookup
+ */
+$enum edg_wll_StringTo${c}(char *name) 
+\{
+       unsigned int    i;
+
+       for (i=1; i<sizeof($char)/sizeof(${char}\[0\]); i++) 
+               if (strcasecmp(${char}\[i\],name) == 0) return ($enum) i;
+       return ($enum) EDG_WLL_${tu}UNDEFINED;
+\}
+\n};
+
+# function ToString:
+                       gen qq{
+/**
+ * \\fn *edg_wll\_${c}ToString($enum code)
+ * Calls: strdup
+ */
+char *edg_wll\_${c}ToString($enum code) 
+\{
+       if ((int)code < 0 || code >= sizeof($char)/sizeof(${char}\[0\])) return NULL;
+       return strdup(${char}\[code\]);
+
+\}
+\n\n};
+               }
+       }
+}
+@@@}
+
+/**
+ * Initialise an event structure
+ * \return pointer to initialised event structure
+ */
+edg_wll_Event *edg_wll_InitEvent(
+               edg_wll_EventCode eventcode     /* eventcode IN */
+) 
+{
+edg_wll_Event *event;
+
+/* allocate memory for 'event' (edg_wll_Event); use calloc to also clean it */
+/* FIXME: what if calloc fails? should be checked somehow -> nomem etc. */
+       event = (edg_wll_Event *) calloc(1,sizeof(edg_wll_Event));
+/* initialize common fields */
+@@@{
+$indent = "\t";
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       my $fn = $f->getName;
+       my $fd = $f->getDefaultNullValue;
+       gen $indent."event->any.$fn = $fd;\n"
+}
+@@@}
+/* initialize dependent fields */
+switch (eventcode) {
+@@@{
+$indent = "   ";
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       gen $indent."case EDG_WLL_EVENT_$tu : \n";
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->getName;
+               my $fd = $f->getDefaultNullValue;
+               gen $indent."\tevent->$tl.$fn = $fd;\n"
+       }
+       gen $indent.$indent."break;\n";
+}
+@@@}
+   case EDG_WLL_EVENT_UNDEF :
+   default :
+      break;
+}
+return event;
+}
+
+/**
+ * edg_wll_FreeEvent 
+ * \brief free memory allocated for edg_wll_Event internal fields
+ * Calls: free
+ */
+void edg_wll_FreeEvent(
+          edg_wll_Event *event       /* event IN */
+)
+{
+edg_wll_EventCode eventcode=event->type;
+
+/* free the common fields */
+        if (event->any.jobId) edg_wlc_JobIdFree(event->any.jobId);
+@@@{
+$indent = "\t";
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       my $fn = $f->{name};
+       my $ft = $f->{type};
+       if ($ft eq 'string') {
+               gen $indent."if (event->any.$fn) free(event->any.$fn);\n"
+       }
+}
+@@@}
+
+/* free the rest */
+switch (eventcode) {
+@@@{
+$indent = "   ";
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       gen $indent."case EDG_WLL_EVENT_$tu :\n";
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $ft = $f->{type};
+               if ($ft eq 'string') {
+                       gen $indent."\tif (event->$tl.$fn) free(event->$tl.$fn);\n"
+               }
+       }
+       gen $indent.$indent."break;\n"
+}
+@@@}
+   case EDG_WLL_EVENT_UNDEF :
+   default:
+      break;
+}
+
+}
diff --git a/org.glite.lb.common/src/events_parse.c.T b/org.glite.lb.common/src/events_parse.c.T
new file mode 100644 (file)
index 0000000..7854152
--- /dev/null
@@ -0,0 +1,732 @@
+#ident "$Header$"
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+
+#include "glite/lb/events.h"
+#include "glite/lb/producer.h"
+#include "events_parse.h"
+#include "ulm_parse.h"
+#include "context-int.h"
+#include "escape.h"
+#include "glite/wms/jobid/cjobid.h"
+
+#include "glite/lb/trio.h"
+
+static const struct timeval null_timeval = {0,0};
+
+/* -- Internal function prototype -- */
+char *my_edg_wll_ULMGetValueAt( p_edg_wll_ULMFields, int );
+
+/**
+ * edg_wll_ParseEvent - parse ULM message to internal structures
+ * Calls: calloc, free, sprintf, strdup
+ *        edg_wll_ULMNewParseTable, edg_wll_ULMFreeParseTable, edg_wll_ULMProcessParseTable,
+ *        edg_wll_ULMGetNameAt, my_edg_wll_ULMGetValueAt, edg_wll_ULMDateToTimeval (edg_wll_ULMDateToDouble),
+ *        edg_wll_StringToEvent, edg_wll_StringToKeyName, edg_wll_StringToLevel, edg_wlc_jobid_tParse,
+ *        edg_wll_EventToString, edg_wll_KeyNameToString, edg_wll_SetError (or edg_wll_ResetError)
+ * Algorithm: break ULM string into fields and then according the event type
+ *            fill in the proper structures
+ */
+
+#define DUPLICITY { \
+       char *k =  edg_wll_KeyNameToString(keycode); \
+       sprintf(err_desc,"Key %s already exists.", k); \
+       free(k); \
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_KEY_DUPLICITY,err_desc); \
+       goto end; }
+
+#define MISUSE { \
+       char *e = edg_wll_EventToString(eventcode); \
+       char *k =  edg_wll_KeyNameToString(keycode); \
+       sprintf(err_desc,"Key %s schouldn't be used for event type %s.", k,e); \
+       free(e); \
+       free(k); \
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_KEY_MISUSE,err_desc); \
+       goto end; }
+
+edg_wll_ErrorCode edg_wll_ParseEvent(
+        edg_wll_Context context,   /* context IN */
+        edg_wll_LogLine logline,     /* logline IN: ULM string to parse */
+        edg_wll_Event **event      /* event OUT: parsed event 
+                           (may be NULL - syntax checking with no output */
+)
+{
+int                    i;
+int                    extra=0;        /* number of extra unknown key=value pairs */
+char                   err_desc[128];  /* error description for use in edg_wll_SetError */
+char                   *value=NULL;
+p_edg_wll_ULMFields    table = edg_wll_ULMNewParseTable(logline);
+edg_wll_KeyNameCode    keycode=UNDEFINED;
+edg_wll_EventCode      eventcode=EDG_WLL_EVENT_UNDEF;
+edg_wll_ErrorCode      ret;
+edg_wll_Event          *this=NULL;
+
+/* break ULM string into fields */
+if ( edg_wll_ULMProcessParseTable(table) != 0 ) { 
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_BROKEN_ULM,"ULM parse error"); 
+       goto end; 
+}
+
+/* determine the event type */
+for (i=0; i<table->num; i++) {
+   keycode=edg_wll_StringToKeyName(edg_wll_ULMGetNameAt(table,i));
+   if ( keycode == EDG_WLL_EVNT ) { 
+       value=my_edg_wll_ULMGetValueAt(table,i); 
+       eventcode=edg_wll_StringToEvent(value); 
+       free(value);
+       value=NULL;
+       break; 
+   }
+}
+if ( eventcode == EDG_WLL_EVENT_UNDEF ) {
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_EVENT_UNDEF,"Unknown or missing event type"); 
+       goto end; 
+}
+/* allocate memory for 'this' (edg_wll_Event) */
+// XXX:  this = (edg_wll_Event *) calloc(1,sizeof(edg_wll_Event)); 
+this = edg_wll_InitEvent(eventcode);
+this->any.type = eventcode;
+
+/* go through all fields and fill in the edg_wll_Event union
+ * in each step check for key duplicity and key name misuse 
+ */
+for (i=0; i<table->num; i++) {
+   keycode=edg_wll_StringToKeyName(edg_wll_ULMGetNameAt(table,i));
+   value=my_edg_wll_ULMGetValueAt(table,i); 
+   switch (keycode) {
+   case EDG_WLL_EVNT : 
+       if (this->any.type != edg_wll_StringToEvent(value)) DUPLICITY
+      break;
+@@@{
+$indent = "   ";
+selectType $event '_common_';
+for ($event->getFieldsOrdered) {
+       my $f = selectField $event $_;
+       my $fn = getName $f;
+       my $fnu = uc getName $f 'ULM';
+       if (hasAlias $f 'ULM') {
+               gen $indent."case ULM\_$fnu :\n";
+       } else {
+               gen $indent."case EDG_WLL\_COMMON\_$fnu :\n";
+       }
+       gen "\tif (";
+               gen $f->isnotNULL("this->any.$fn");
+               gen ") DUPLICITY \n";
+       gen "\t";
+       if ($f->{codes}) {
+               my $c = ucfirst(${fn});
+               gen "this->any.$fn = edg_wll_StringTo${c}(value);";
+       } else {
+               gen $f->fromString('value',"this->any.$fn");
+       }
+       gen "\n".$indent.$indent."break;\n";
+}
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       my $misuse = "if (eventcode != EDG_WLL_EVENT_$tu ) MISUSE";
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $fnu = uc $fn;
+               gen $indent."case EDG_WLL\_$tu\_$fnu :\n";
+               gen "\tif ("; 
+                       gen $f->isnotNULL("this->$tl.$fn");
+                       gen ") DUPLICITY\n";
+               gen "\t$misuse\n";
+               if ($f->{codes}) {
+                       my $c = "$t".ucfirst(${fn});
+                       gen "\tthis->$tl.$fn = edg_wll_StringTo${c}(value);";
+               } else {
+                       gen "\t";
+                       gen $f->fromString('value',"this->$tl.$fn");
+               }
+               gen "\n".$indent.$indent."break;\n";
+       }
+}
+@@@}
+
+   case UNDEFINED :
+   case EDG_WLL_INTERNAL_TYPE :
+      break;
+
+   default : 
+       extra++;
+      break;
+   }
+   free(value);
+   value=NULL;
+}
+
+/* now check if all required fields are present */
+ret=edg_wll_CheckEvent(context,this);
+if (ret) { goto end; }
+
+/* parse is OK, only extra fields could occur */
+if (extra) {
+       sprintf(err_desc,"There are %d extra fields in the logline.",extra);
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_OK_WITH_EXTRA_FIELDS,err_desc); }
+else { ret=edg_wll_ResetError(context); }
+
+end:
+   /* finally (if wanted) "return" pointer to the filled edg_wll_Event union
+      NOTE: a pointer (to half-filled structure) is returned even if an error occured ! */
+   if (event != NULL) {
+       *event = this; }
+       /* This also means, that the memory must be freed by edg_wll_FreeEvent() somewhere else */
+   else {
+       edg_wll_FreeEvent(this);
+       free(this);
+   }
+
+   edg_wll_ULMFreeParseTable(table);
+   return ret;
+}
+
+/**
+ * edg_wll_UnparseEvent - unparse ULM message from internal structures
+ * Calls: malloc, free, strlen, strcmp, asprintf, trio_asprintf
+ *        edg_wll_ULMTimevalToDate,
+ *        edg_wll_EventToString, edg_wll_KeyNameToString, edg_wll_LevelToString, 
+ *        edg_wlc_jobid_tUnparse, 
+ * Algorithm: format values from internal structures into a ULM string
+ */
+#define NOMEM { if (logline) free(logline); logline = NULL; edg_wll_SetError(context,ENOMEM,NULL); goto clean; }
+
+edg_wll_LogLine edg_wll_UnparseEvent(       /* logline OUT */
+          edg_wll_Context context,    /* context IN */
+          edg_wll_Event *event        /* event IN */
+)
+{
+edg_wll_LogLine                logline;
+edg_wll_EventCode      eventcode=event->type;
+char                   *date, *common, *user, *var, *e, *l, *j, *s;
+edg_wll_Event          nonulls;
+
+logline = date = common = user = var = NULL;
+
+/* FIXME: after EDG_WLL_FORMAT_COMMON automatic generation automate this code as well */
+/* format the common fields */
+date=(char *) malloc(1+ULM_DATE_STRING_LENGTH*sizeof(char));
+edg_wll_ULMTimevalToDate(event->any.timestamp.tv_sec, event->any.timestamp.tv_usec, date);
+
+memcpy(&nonulls,event,sizeof nonulls);
+@@@{
+       selectType $event '_common_';
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               gen "if (!nonulls.any.$fn) nonulls.any.$fn = \"\";\n"
+                       if $f->{type} eq 'string';
+       }
+@@@}
+
+e = edg_wll_EventToString(eventcode);
+l = edg_wll_LevelToString(event->any.level);
+j = edg_wlc_JobIdUnparse(event->any.jobId);
+s = edg_wll_SourceToString(event->any.source);
+if (trio_asprintf(&common,EDG_WLL_FORMAT_COMMON, \
+       date, event->any.host, l, event->any.priority, s, nonulls.any.src_instance, \
+       e, j, nonulls.any.seqcode) == -1) { 
+       if (e) free(e);
+       if (l) free(l);
+       if (j) free(j);
+       if (s) free(s);
+       NOMEM 
+}
+if (e) free(e);
+if (l) free(l);
+if (j) free(j);
+if (s) free(s);
+
+if (trio_asprintf(&user,EDG_WLL_FORMAT_USER, event->any.user) == -1) NOMEM
+
+/* format the rest of the logline */
+switch (eventcode) {
+@@@{
+$indent = "   ";
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       my $free = "";
+       gen $indent."case EDG_WLL_EVENT_$tu :\n";
+       gen "\t\{";
+       selectType $event $t;
+# if there are some _code_, convert them from string:
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $fnu = ucfirst $fn;
+               if ($f->{codes}) {
+                       gen "\tchar \*$fn\_code = edg_wll\_$t${fnu}ToString(event->$tl.$fn);\n"; 
+                       $free = $free . "free($fn\_code); ";
+               }
+               if ($f->{type} eq 'jobid') {
+                       gen "\tchar \*$fn\_str = edg_wlc_JobIdUnparse(event->$tl.$fn);\n";
+                       $free .= "free($fn\_str); ";
+               }
+               if ($f->{type} eq 'notifid') {
+                       gen "\tchar \*$fn\_str = edg_wll_NotifIdUnparse(event->$tl.$fn);\n";
+                       $free .= "free($fn\_str); ";
+               }
+               if ($ULMasString{$f->{type}}) {
+                       gen "\tchar \*$fn\_str = ".$f->getType()."ToString(event->$tl.$fn);\n";
+                       $free .= "free($fn\_str); ";
+               }
+       }
+       
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+
+               gen "\t\tif (!nonulls.$tl.$fn) nonulls.$tl.$fn = \"\";\n"
+                       if $f->{type} eq 'string';
+       }
+       gen "\tif (trio_asprintf(&var,EDG_WLL_FORMAT_$tu";
+# now format remaining parapteres for trio_asprintf:
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               if ($f->{codes}) {
+                       gen ",$fn\_code";
+               }
+               elsif ($f->{type} eq 'jobid' || $f->{type} eq 'notifid' || $ULMasString{$f->{type}}) {
+                       gen ",$fn\_str?$fn\_str:\"\"";
+               } else {
+                       gen ",nonulls.$tl.$fn";
+               }
+       }
+       gen ") == -1 ) \{\n";
+               gen "\t".$indent.$free."NOMEM \}\n";
+       if ($free) { gen "\t$free\n"; }
+       gen "\t\}\n";
+       gen $indent.$indent."break;\n";
+}
+@@@}
+  case EDG_WLL_EVENT_UNDEF :
+  default :
+     break;
+}
+
+/* put it all together (inc. the ending LF) */
+if (asprintf(&logline,"%s%s%s\n",common,user,var) == -1) NOMEM
+
+clean:
+       if (date) free(date);
+       if (common) free(common);
+       if (user) free(user);
+       if (var) free(var);
+       return logline;
+}
+
+
+/**
+ * edg_wll_CheckEvent - check internal structures if all required fields are present
+ * Calls: free, sprintf, strcmp
+ *        edg_wll_KeyNameToString, edg_wll_SetError (or edg_wll_ResetError)
+ * Algorithm:
+ */
+#define MISSING(m_key) { \
+       char *k =  edg_wll_KeyNameToString(m_key); \
+        sprintf(err_desc,"Message incomplete, missing key %s.", k); \
+       free(k); \
+        ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_MSG_INCOMPLETE,err_desc); \
+        goto end; }
+
+edg_wll_ErrorCode edg_wll_CheckEvent(
+       edg_wll_Context context,        /* context IN */
+       edg_wll_Event *event    /* event IN */
+)
+{
+char err_desc[128];            /* error description for use in edg_wll_SetError */
+edg_wll_EventCode eventcode=EDG_WLL_EVENT_UNDEF;
+edg_wll_ErrorCode ret;
+
+eventcode=event->type;
+if ( eventcode == EDG_WLL_EVENT_UNDEF ) {
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_EVENT_UNDEF,"Unknown or missing event type"); 
+       goto end; 
+}
+
+/*
+FIXME:
+?      if (!event->any.user) MISSING(EDG_WLL_USER) - cannot use in edg_wll_LogEvent(), where is no DG.USER
+*/
+
+@@@{
+       $indent = "   ";
+       selectType $event '_common_';
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               next if $f->{optional};
+               if (!$f->{codes}) {
+                       if (!$f->hasAlias('ULM')) {
+                               my $fu = uc $fn;
+                               gen "\tif (". $f->isNULL("event->any.$fn") .") MISSING(EDG_WLL\_COMMON\_$fu)\n";
+                       } else {
+                               my $fa = $f->getName('ULM');
+                               my $fu = uc $fa;
+                               gen "\tif (". $f->isNULL("event->any.$fn") .") MISSING(ULM\_$fu)\n";
+                       }
+               }
+       }
+@@@}
+
+switch (eventcode) {
+@@@{
+$indent = "   ";
+for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+               $event->getTypes) {
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       gen $indent."case EDG_WLL_EVENT_$tu :\n";
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               next if $f->{optional};
+               if (!$f->{codes}) {
+                       my $fn = $f->{name};
+                       my $fu = uc $fn;
+                       my $ft = $f->{type};
+                       gen "\tif (". $f->isNULL("event->$tl.$fn") .") MISSING(EDG_WLL\_$tu\_$fu)\n";
+               }
+       }
+       gen $indent.$indent."break;\n";
+}
+@@@}
+
+   case EDG_WLL_EVENT_UNDEF :
+   default:
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_EVENT_UNDEF,"Unknown or missing event type"); 
+        goto end;
+      break;
+};
+ret=edg_wll_ResetError(context);
+
+end:
+       return ret;
+}
+
+
+/**
+ * my_edg_wll_ULMGetValueAt - get value at index, but also
+ *                - remove surrounding quotes if any
+ *                - and unescape the string
+ *                - the new string will be allocated (by edg_wll_LogUnescape)
+ * Calls: edg_wll_ULMGetValueAt, edg_wll_LogUnescape, calloc and strncpy or strdup
+ */
+char *my_edg_wll_ULMGetValueAt( p_edg_wll_ULMFields table, int index )
+{
+char *str = edg_wll_ULMGetValueAt( table, index);
+size_t len = strlen(str);
+char *ret,*first,*last,*tmp;
+
+first = last = str;
+last += len - 1;
+
+/* remove surrounding quotes */
+if ((*first == ULM_QM) && (*last == ULM_QM)) {
+       /* be careful with doppel_ULM_QM */
+       if (len > 2) {
+               tmp = (char *) calloc (1,(len-1)*sizeof(char));
+               strncpy(tmp,first+1,len-2);
+               tmp[len-2] = '\0';
+       }
+       else {
+               tmp = strdup("");
+       }
+}
+else tmp = strdup(str);
+
+if (len > 2) {
+       ret = edg_wll_LogUnescape(tmp);
+       if (tmp) free(tmp);
+}
+else ret = tmp;
+
+return ret;
+}
+
+
+/**
+ * edg_wll_GetJobId - parse jobId from ULM message
+ * Returns: edg_wlc_jobid_t string or NULL
+ * Calls: edg_wll_ULMNewParseTable, edg_wll_ULMFreeParseTable, edg_wll_ULMProcessParseTable,
+ *        edg_wll_ULMGetNameAt, my_edg_wll_ULMGetValueAt, edg_wlc_jobid_tParse, strdup
+ * Algorithm: break ULM string into fields and then look for jobId
+ */
+char *edg_wll_GetJobId(edg_wll_LogLine logline)
+{
+p_edg_wll_ULMFields table = edg_wll_ULMNewParseTable(logline);
+int i;
+char *ret=NULL,*fullid=NULL;
+edg_wll_KeyNameCode keycode=UNDEFINED;
+edg_wlc_JobId jobId = NULL;
+
+/* break ULM string into fields */
+if ( edg_wll_ULMProcessParseTable(table) != 0 ) goto clean;
+
+/* look for jobId */
+for (i=0; i<table->num; i++) {
+       keycode=edg_wll_StringToKeyName(edg_wll_ULMGetNameAt(table,i));
+       if ( keycode == EDG_WLL_COMMON_JOBID ) { fullid=my_edg_wll_ULMGetValueAt(table,i); break; }
+}
+if ( fullid == NULL ) goto clean;
+
+if (edg_wlc_JobIdParse(fullid, &jobId)) goto clean;
+
+ret = strdup(fullid);
+
+clean:
+   if (fullid) free(fullid);
+   if (jobId) edg_wlc_JobIdFree(jobId);
+   edg_wll_ULMFreeParseTable(table);
+   return ret;
+}
+
+/**
+ * Parse a special Notification ULM line into a edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param logline IN: ULM string to parse
+ * \param event OUT: parsed event
+ *     (may be NULL - syntax checking with no output)
+ */
+edg_wll_ErrorCode edg_wll_ParseNotifEvent(
+       edg_wll_Context context,
+       edg_wll_LogLine logline,
+       edg_wll_Event ** event
+)
+{
+int                    i;
+int                    extra=0;        /* number of extra unknown key=value pairs */
+char                   err_desc[128];  /* error description for use in edg_wll_SetError */
+char *value=NULL;
+p_edg_wll_ULMFields    table = edg_wll_ULMNewParseTable(logline);
+edg_wll_KeyNameCode    keycode=UNDEFINED;
+const edg_wll_EventCode        eventcode=EDG_WLL_EVENT_NOTIFICATION;
+edg_wll_ErrorCode      ret;
+edg_wll_Event          *this=NULL;
+
+/* break ULM string into fields */
+if ( edg_wll_ULMProcessParseTable(table) != 0 ) {
+       ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_BROKEN_ULM,"ULM parse error");
+       goto end;
+}
+
+/* allocate memory for 'this' (edg_wll_Event) */
+this = edg_wll_InitEvent(eventcode);
+this->any.type = eventcode;
+
+/* go through all fields and fill in the edg_wll_Event union
+ * in each step check for key duplicity and key name misuse 
+ */
+for (i=0; i<table->num; i++) {
+   keycode=edg_wll_StringToKeyName(edg_wll_ULMGetNameAt(table,i));
+   value=my_edg_wll_ULMGetValueAt(table,i); 
+   switch (keycode) {
+   case EDG_WLL_EVNT : 
+       if (this->any.type != edg_wll_StringToEvent(value)) DUPLICITY
+      break;
+   case ULM_DATE :
+       if (!((this->any.timestamp).tv_sec == (null_timeval).tv_sec && (this->any.timestamp).tv_usec == (null_timeval).tv_usec)) DUPLICITY 
+       edg_wll_ULMDateToTimeval(value,&this->any.timestamp);
+      break;
+   case ULM_ARR_DATE :
+       if (!((this->any.arrived).tv_sec == (null_timeval).tv_sec && (this->any.arrived).tv_usec == (null_timeval).tv_usec)) DUPLICITY 
+       edg_wll_ULMDateToTimeval(value,&this->any.arrived);
+      break;
+   case ULM_HOST :
+       if (!(((this->any.host) == NULL && (NULL) == NULL) || ((this->any.host)&&(NULL)&& !strcmp(this->any.host,NULL)))) DUPLICITY 
+       this->any.host = strdup(value);
+      break;
+   case ULM_LVL :
+       if (!((this->any.level == 0))) DUPLICITY 
+       this->any.level = edg_wll_StringToLevel(value);
+      break;
+   case EDG_WLL_COMMON_SOURCE :
+       if (!((this->any.source) == (EDG_WLL_SOURCE_NONE))) DUPLICITY 
+       this->any.source = edg_wll_StringToSource(value);
+      break;
+   case EDG_WLL_COMMON_SRC_INSTANCE :
+       if (!(((this->any.src_instance) == NULL && (NULL) == NULL) || ((this->any.src_instance)&&(NULL)&& !strcmp(this->any.src_instance,NULL)))) DUPLICITY 
+       this->any.src_instance = strdup(value);
+      break;
+@@@{
+       $indent = "   ";
+       my $t = 'Notification';
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       my $misuse = "if (eventcode != EDG_WLL_EVENT_$tu ) MISUSE";
+       selectType $event $t;
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $fnu = uc $fn;
+               gen $indent."case EDG_WLL\_$tu\_$fnu :\n";
+               gen "\tif ("; 
+                       gen $f->isnotNULL("this->$tl.$fn");
+                       gen ") DUPLICITY\n";
+               gen "\t$misuse\n";
+               if ($f->{codes}) {
+                       my $c = "$t".ucfirst(${fn});
+                       gen "\tthis->$tl.$fn = edg_wll_StringTo${c}(value);";
+               } else {
+                       gen "\t";
+                       gen $f->fromString('value',"this->$tl.$fn");
+               }
+               gen "\n".$indent.$indent."break;\n";
+       }
+@@@}
+
+   case UNDEFINED :
+   case EDG_WLL_INTERNAL_TYPE :
+      break;
+
+   default : 
+       extra++;
+      break;
+   }
+   free(value);
+   value=NULL;
+}
+
+/* parse is OK, only extra fields could occur */
+if (extra) {
+        sprintf(err_desc,"There are %d extra fields in the logline.",extra);
+        ret=edg_wll_SetError(context,EDG_WLL_ERROR_PARSE_OK_WITH_EXTRA_FIELDS,err_desc); }
+else {  ret=edg_wll_ResetError(context); }
+
+end:
+   /* finally (if wanted) "return" pointer to the filled edg_wll_Event union
+      NOTE: a pointer (to half-filled structure) is returned even if an error occured ! */
+   if (event != NULL) {
+       *event = this; }
+       /* This also means, that the memory must be freed by edg_wll_FreeEvent() somewhere else */
+   else {
+       edg_wll_FreeEvent(this);
+       free(this);
+   }
+
+   edg_wll_ULMFreeParseTable(table);
+   return ret;
+}
+
+/** 
+ * Generate a special Notification ULM line from edg_wll_Event structure
+ * \param context IN: context to work with
+ * \param event IN: event to unparse
+ */
+edg_wll_LogLine edg_wll_UnparseNotifEvent(
+       edg_wll_Context context,
+       edg_wll_Event * event
+)
+{
+edg_wll_LogLine logline;
+char           *date, *common, *var, *l, *s;
+edg_wll_Event   nonulls;
+
+logline = date = common = var = l = s = NULL;
+
+/* format the common fields */
+date=(char *) malloc(1+ULM_DATE_STRING_LENGTH*sizeof(char));
+edg_wll_ULMTimevalToDate(event->any.timestamp.tv_sec, event->any.timestamp.tv_usec, date);
+
+memcpy(&nonulls,event,sizeof nonulls);
+if (!nonulls.any.host) nonulls.any.host = "";
+if (!nonulls.any.src_instance) nonulls.any.src_instance = "";
+
+l = edg_wll_LevelToString(event->any.level);
+s = edg_wll_SourceToString(event->any.source);
+if (trio_asprintf(&common,EDG_WLL_FORMAT_NOTIFICATION_COMMON, \
+        date, event->any.host, l, s, nonulls.any.src_instance) == -1) {
+               if (l) free(l);
+               if (s) free(s);
+               NOMEM 
+       }
+if (l) free(l);
+if (s) free(s);
+
+// n = edg_wlc_NotifIdUnparse(event->notification.notifId);
+
+/* format the rest of the logline */
+@@@{
+$indent = "   ";
+       my $t = 'Notification';
+       my $tu = uc $t;
+       my $tl = lcfirst $t;
+       my $free = "";
+##     gen $indent."case EDG_WLL_EVENT_$tu :\n";
+       gen "\t\{";
+       selectType $event $t;
+# if there are some _code_, convert them from string:
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               my $fnu = ucfirst $fn;
+               if ($f->{codes}) {
+                       gen "\tchar \*$fn\_code = edg_wll\_$t${fnu}ToString(event->$tl.$fn);\n"; 
+                       $free = $free . "free($fn\_code); ";
+               }
+               if ($f->{type} eq 'jobid') {
+                       gen "\tchar \*$fn\_str = edg_wlc_JobIdUnparse(event->$tl.$fn);\n";
+                       $free .= "free($fn\_str); ";
+               }
+               if ($f->{type} eq 'notifid') {
+                       gen "\tchar \*$fn\_str = edg_wll_NotifIdUnparse(event->$tl.$fn);\n";
+                       $free .= "free($fn\_str); ";
+               }
+               if ($ULMasString{$f->{type}}) {
+                       gen "\tchar \*$fn\_str = ".$f->getType()."ToString(event->$tl.$fn);\n";
+                       $free .= "free($fn\_str); ";
+               }
+       }
+       
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+
+               gen "\t\tif (!nonulls.$tl.$fn) nonulls.$tl.$fn = \"\";\n"
+                       if $f->{type} eq 'string';
+       }
+       gen "\tif (trio_asprintf(&var,EDG_WLL_FORMAT_$tu";
+# now format remaining parapteres for trio_asprintf:
+       for ($event->getFieldsOrdered) {
+               my $f = selectField $event $_;
+               my $fn = $f->{name};
+               if ($f->{codes}) {
+                       gen ",$fn\_code";
+               }
+               elsif ($f->{type} eq 'jobid' || $f->{type} eq 'notifid' || $ULMasString{$f->{type}}) {
+                       gen ",$fn\_str?$fn\_str:\"\"";
+               } else {
+                       gen ",nonulls.$tl.$fn";
+               }
+       }
+       gen ") == -1 ) \{\n";
+               gen "\t".$indent.$free."NOMEM \}\n";
+       if ($free) { gen "\t$free\n"; }
+       gen "\t\}\n";
+##     gen $indent.$indent."break;\n";
+@@@}
+
+/* put it all together (inc. the ending LF) */
+if (asprintf(&logline,"%s%s\n",common,var) == -1) NOMEM
+
+clean:
+        if (date) free(date);
+        if (common) free(common);
+        if (var) free(var);
+        return logline;
+}
+
diff --git a/org.glite.lb.common/src/il_int.c b/org.glite.lb.common/src/il_int.c
new file mode 100644 (file)
index 0000000..7d4bdf5
--- /dev/null
@@ -0,0 +1,69 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "il_string.h"
+
+char *
+_put_int(char *p, int d)
+{
+  char buf[32];
+  int len;
+  
+  assert( p != NULL );
+
+  snprintf(buf, sizeof(buf), "%d", d);
+  len = strlen(buf);
+  strncpy(p, buf, len);
+  return(p + len);
+}
+
+
+char *
+put_int(char *p, int d)
+{
+  assert( p != NULL );
+
+  p = _put_int(p, d);
+  *p++ = '\n';
+  return(p);
+}
+
+
+char *
+_get_int(char *p, int *d)
+{
+   char *end;
+
+  assert( p != NULL );
+  assert( d != NULL );
+
+  *d = strtol(p, &end, 10);
+  return(end);
+}
+  
+
+char *
+get_int(char *p, int *d)
+{
+  assert( p != NULL );
+  assert( d != NULL );
+
+  p = _get_int(p, d);
+  if(*p != '\n') 
+    return(NULL);
+  else
+    return(p + 1);
+}
+
+
+int 
+len_int(int d)
+{
+  char buffer[256];
+
+  return(put_int(buffer, d) - buffer);
+}
diff --git a/org.glite.lb.common/src/il_log.c b/org.glite.lb.common/src/il_log.c
new file mode 100644 (file)
index 0000000..11170c5
--- /dev/null
@@ -0,0 +1,32 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+int log_level;
+
+int
+il_log(int level, char *fmt, ...)
+{
+       char *err_text;
+       va_list fmt_args;
+
+       va_start(fmt_args, fmt);
+       vasprintf(&err_text, fmt, fmt_args);
+       va_end(fmt_args);
+       
+       if(level <= log_level) 
+               fprintf(stderr, err_text);
+       
+       if(level <= LOG_ERR) {
+               openlog("edg-wl-interlogd", LOG_PID | LOG_CONS, LOG_DAEMON);
+               syslog(level, "%s", err_text);
+               closelog();
+       }
+
+       if(err_text) free(err_text);
+
+       return(0);
+}
diff --git a/org.glite.lb.common/src/il_msg.c b/org.glite.lb.common/src/il_msg.c
new file mode 100644 (file)
index 0000000..30997f6
--- /dev/null
@@ -0,0 +1,53 @@
+#ident "$Header$"
+
+#include "il_string.h"
+
+#include <malloc.h>
+#include <stdio.h>
+#include <unistd.h>
+
+int 
+receive_msg(int sd, char **ucs, char **event)
+{
+  char buffer[17];
+  char *p, *q, *msg;
+  int len;
+
+  len = read(sd, buffer, 17);
+
+  if(buffer[16] != '\n') {
+    printf("Error in header!\n");
+    goto err;
+  }
+
+  sscanf(buffer, "%d", &len);
+  if(len > MAXLEN) {
+    printf("Message too long!\n");
+    goto err;
+  }
+  p = msg = malloc(len+1);
+  if(p == NULL) {
+    printf("Error allocating %d bytes\n", len+1);
+    goto err;
+  }
+
+  read(sd, p, len);
+  p[len] = 0;
+
+  if((q = get_string(p, ucs)) == NULL) {
+    printf("Protocol error at %s\n", p);
+    goto err;
+  }
+  p = q;
+  if((q = get_string(p, event)) == NULL) {
+    printf("Protocol error at %s\n", p);
+    goto err;
+  }
+
+  free(msg);
+  return(0);
+
+ err: 
+  
+  return(-1);
+}
diff --git a/org.glite.lb.common/src/il_string.c b/org.glite.lb.common/src/il_string.c
new file mode 100644 (file)
index 0000000..2c4034d
--- /dev/null
@@ -0,0 +1,61 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "il_string.h"
+
+char *
+put_string(char *p, char *s)
+{
+  int len = strlen(s);
+  
+  assert( p != NULL );
+
+  p = _put_int(p, len);
+  *p++ = ' ';
+  strncpy(p, s, len);
+  p += len;
+  *p++ = '\n';
+  return(p);
+}
+
+
+int
+len_string(char *s)
+{
+  int len, slen;
+
+  assert( s != NULL );
+
+  slen = strlen(s);
+  len = len_int(slen);
+
+  return(len + slen + 1);
+}
+
+
+char *
+get_string(char *p, char **s)
+{
+  int len;
+
+  assert( p != NULL );
+
+  *s = NULL;
+
+  p = _get_int(p, &len);
+  if(*p != ' ')
+    return(NULL);
+  else
+    {
+      *s = malloc(len + 1);
+      if(*s == NULL)
+       return(NULL);
+      strncpy(*s, ++p, len);
+      (*s)[len] = '\0';
+      p += len;
+      return( *p++ == '\n' ? p : NULL );
+    }
+}
diff --git a/org.glite.lb.common/src/mini_http.c b/org.glite.lb.common/src/mini_http.c
new file mode 100644 (file)
index 0000000..722e246
--- /dev/null
@@ -0,0 +1,224 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <syslog.h>
+
+#include "globus_config.h"
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include "mini_http.h"
+#include "dgssl.h"
+#include "context-int.h"
+
+#define min(x,y)       ((x) < (y) ? (x) : (y))
+#define CONTENT_LENGTH "Content-Length:"
+
+edg_wll_ErrorCode edg_wll_http_recv(edg_wll_Context ctx,char **firstOut,char ***hdrOut,char **bodyOut)
+{
+       char    **hdr = NULL,*first = NULL,*body = NULL;
+       enum    { FIRST, HEAD, BODY, DONE }     pstat = FIRST;
+       int     len, nhdr = 0,rdmore = 0,clen = 0,blen = 0;
+       int     sock;
+
+#define bshift(shift) {\
+       memmove(ctx->connPool[ctx->connToUse].buf,ctx->connPool[ctx->connToUse].buf+(shift),ctx->connPool[ctx->connToUse].bufUse-(shift));\
+       ctx->connPool[ctx->connToUse].bufUse -= (shift);\
+}
+       edg_wll_ResetError(ctx);
+
+
+       if (ctx->connPool[ctx->connToUse].ssl) sock = SSL_get_fd(ctx->connPool[ctx->connToUse].ssl);
+       else { 
+               edg_wll_SetError(ctx,ENOTCONN,NULL);
+               goto error;
+       }
+
+       if (!ctx->connPool[ctx->connToUse].buf) ctx->connPool[ctx->connToUse].buf = malloc(ctx->connPool[ctx->connToUse].bufSize = BUFSIZ);
+
+       do {
+               len = edg_wll_ssl_read(ctx->connPool[ctx->connToUse].ssl,
+                       ctx->connPool[ctx->connToUse].buf+ctx->connPool[ctx->connToUse].bufUse,ctx->connPool[ctx->connToUse].bufSize-ctx->connPool[ctx->connToUse].bufUse,&ctx->p_tmp_timeout);
+
+               switch (len) {
+                       case EDG_WLL_SSL_OK:
+                       case EDG_WLL_SSL_ERROR_SSL:
+                               edg_wll_SetError(ctx,EDG_WLL_ERROR_SSL,
+                                       ERR_error_string(ERR_get_error(), NULL));
+                               goto error;
+                       case EDG_WLL_SSL_ERROR_ERRNO:
+                               edg_wll_SetError(ctx,errno,"edg_wll_ssl_read()");
+                               goto error;
+                       case EDG_WLL_SSL_ERROR_TIMEOUT:
+                               edg_wll_SetError(ctx,ETIMEDOUT,NULL);
+                               goto error; 
+                       case EDG_WLL_SSL_ERROR_EOF:
+                               edg_wll_SetError(ctx,ENOTCONN,NULL);
+                               goto error; 
+                       /* default: fallthrough */
+               }
+
+
+               ctx->connPool[ctx->connToUse].bufUse += len;
+               rdmore = 0;
+
+               while (!rdmore && pstat != DONE) switch (pstat) {
+                       char    *cr; 
+
+                       case FIRST:
+                               if ((cr = memchr(ctx->connPool[ctx->connToUse].buf,'\r',ctx->connPool[ctx->connToUse].bufUse)) &&
+                                       ctx->connPool[ctx->connToUse].bufUse >= cr-ctx->connPool[ctx->connToUse].buf+2 && cr[1] == '\n')
+                               {
+                                       *cr = 0;
+                                       first = strdup(ctx->connPool[ctx->connToUse].buf);
+                                       bshift(cr-ctx->connPool[ctx->connToUse].buf+2);
+                                       pstat = HEAD;
+                               } else rdmore = 1;
+                               break;
+                       case HEAD:
+                               if ((cr = memchr(ctx->connPool[ctx->connToUse].buf,'\r',ctx->connPool[ctx->connToUse].bufUse)) &&
+                                       ctx->connPool[ctx->connToUse].bufUse >= cr-ctx->connPool[ctx->connToUse].buf+2 && cr[1] == '\n')
+                               {
+                                       if (cr == ctx->connPool[ctx->connToUse].buf) {
+                                               bshift(2);
+                                               pstat = clen ? BODY : DONE;
+                                               if (clen) body = malloc(clen+1);
+                                               break;
+                                       }
+
+                                       *cr = 0;
+                                       hdr = realloc(hdr,(nhdr+2) * sizeof(*hdr));
+                                       hdr[nhdr] = strdup(ctx->connPool[ctx->connToUse].buf);
+                                       hdr[++nhdr] = NULL;
+
+                                       if (!strncasecmp(ctx->connPool[ctx->connToUse].buf,CONTENT_LENGTH,sizeof(CONTENT_LENGTH)-1))
+                                               clen = atoi(ctx->connPool[ctx->connToUse].buf+sizeof(CONTENT_LENGTH)-1);
+       
+                                       bshift(cr-ctx->connPool[ctx->connToUse].buf+2);
+                               } else rdmore = 1;
+                               break;
+                       case BODY:
+                               if (ctx->connPool[ctx->connToUse].bufUse) {
+                                       int     m = min(ctx->connPool[ctx->connToUse].bufUse,clen-blen);
+                                       memcpy(body+blen,ctx->connPool[ctx->connToUse].buf,m);
+                                       blen += m;
+                                       bshift(m);
+                               }
+                               rdmore = 1;
+                               if (blen == clen) {
+                                       pstat = DONE;
+                                       body[blen] = 0;
+                               }
+                               break;
+                       default:
+                               break;
+               }
+       } while (pstat != DONE);
+
+error:
+       if (edg_wll_Error(ctx,NULL,NULL)) {
+               if (hdr) {
+                       char    **h;
+                       for (h = hdr; *h; h++) free(*h);
+                       free(hdr);
+               }
+               free(first);
+               free(body);
+       } else {
+               if (firstOut) *firstOut = first; else free(first);
+               if (hdrOut) *hdrOut = hdr; 
+               else if (hdr) {
+                       char    **h;
+                       for (h = hdr; *h; h++) free(*h);
+                       free(hdr);
+               }
+               if (bodyOut) *bodyOut = body; else free(body);
+       }
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static int real_write(edg_wll_Context ctx, SSL *ssl,int fd,const char *data,int len)
+{
+       int     once,total = 0;
+       struct sigaction        sa,osa;
+
+       memset(&sa,0,sizeof(sa)); assert(sa.sa_handler == NULL);
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGPIPE,&sa,&osa);
+
+       while (total < len) {
+               int     sslerr;
+
+               switch (once = edg_wll_ssl_write(ssl,(void*)(data+total),len-total,&ctx->p_tmp_timeout)) {
+                       case EDG_WLL_SSL_ERROR_EOF:
+                               errno = ENOTCONN;
+                               total = -1;
+                               goto end;
+                       case EDG_WLL_SSL_ERROR_TIMEOUT:
+                               errno = ETIMEDOUT;
+                               total = -1;
+                               goto end;
+                       case EDG_WLL_SSL_ERROR_ERRNO:
+                               if (errno == EPIPE) errno = ENOTCONN;
+                               total = -1;
+                               goto end;
+                       case EDG_WLL_SSL_OK:
+                       case EDG_WLL_SSL_ERROR_SSL:
+                               sslerr = SSL_get_error(ssl,once);
+                               if (sslerr == SSL_ERROR_SYSCALL) {
+                                       if (errno == EPIPE) errno = ENOTCONN;
+                               }
+                               else errno = EDG_WLL_ERROR_SSL;
+                               total = -1;
+                               goto end;
+                       default:
+                               total += once;
+               }
+       }
+end:
+       sigaction(SIGPIPE,&osa,NULL);
+       return total;
+}
+
+edg_wll_ErrorCode edg_wll_http_send(edg_wll_Context ctx,const char *first,const char * const *head,const char *body)
+{
+       const char* const *h;
+       int     len = 0, blen, sock;
+
+       edg_wll_ResetError(ctx);
+
+        if (ctx->connPool[ctx->connToUse].ssl) sock = SSL_get_fd(ctx->connPool[ctx->connToUse].ssl);
+        else return edg_wll_SetError(ctx,ENOTCONN,NULL);
+
+       if (real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,first,strlen(first)) < 0 ||
+               real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,"\r\n",2) < 0) 
+               return edg_wll_SetError(ctx,errno,"edg_wll_http_send()");
+
+       if (head) for (h=head; *h; h++) 
+               if (real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,*h,strlen(*h)) < 0 ||
+                       real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,"\r\n",2) < 0) 
+                       return edg_wll_SetError(ctx,errno,"edg_wll_http_send()");
+
+       if (body) {
+               char    buf[100];
+
+               len = strlen(body);
+               blen = sprintf(buf,CONTENT_LENGTH " %d\r\n",len);
+               if (real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,buf,blen) < 0) 
+                       return edg_wll_SetError(ctx,errno,"edg_wll_http_send()");
+       }
+
+       if (real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,"\r\n",2) < 0) 
+               return edg_wll_SetError(ctx,errno,"edg_wll_http_send()");
+       if (body && real_write(ctx,ctx->connPool[ctx->connToUse].ssl,sock,body,len) < 0)  
+               return edg_wll_SetError(ctx,errno,"edg_wll_http_send()");
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.common/src/notifid.c b/org.glite.lb.common/src/notifid.c
new file mode 100644 (file)
index 0000000..0904325
--- /dev/null
@@ -0,0 +1,88 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "notifid.h"
+
+int edg_wll_NotifIdParse(const char *s,edg_wll_NotifId *n)
+{
+       edg_wlc_JobId   j;
+       int             ret = edg_wlc_JobIdParse(s,&j);
+
+       if (!ret) *n = (edg_wll_NotifId) j; 
+       return ret;
+}
+
+char* edg_wll_NotifIdUnparse(const edg_wll_NotifId n)
+{
+       return edg_wlc_JobIdUnparse((edg_wlc_JobId) n);
+}
+
+int edg_wll_NotifIdCreate(const char *server,int port,edg_wll_NotifId *n)
+{
+       edg_wlc_JobId   j,j2;
+       int     ret = edg_wlc_JobIdCreate(server,port,&j);
+       char    *u,*u2;
+
+       if (!ret) {
+               u = edg_wlc_JobIdGetUnique(j);
+               asprintf(&u2,"NOTIF:%s",u);
+               free(u);
+               ret = edg_wlc_JobIdRecreate(server,port,u2,&j2);
+               free(u2);
+               edg_wlc_JobIdFree(j);
+               if (!ret) *n = (edg_wll_NotifId) j2;
+       }
+
+       return ret;
+}
+
+int edg_wll_NotifIdSetUnique(edg_wll_NotifId *n, const char *un)
+{
+       char               *aux, *srv;
+       int                             ret;
+       unsigned int    port;
+
+
+       asprintf(&aux, "NOTIF:%s", un);
+       if ( !aux )
+               return -1;
+
+       edg_wll_NotifIdGetServerParts(*((edg_wlc_JobId *)n), &srv, &port);
+       ret = edg_wlc_JobIdRecreate(srv, port, aux, (edg_wlc_JobId *)n);
+       free(aux);
+       free(srv);
+
+       return ret;
+}
+
+void edg_wll_NotifIdFree(edg_wll_NotifId n)
+{
+       edg_wlc_JobIdFree((edg_wlc_JobId) n);
+}
+
+void edg_wll_NotifIdGetServerParts(const edg_wll_NotifId notifId, char **srvName, unsigned int *srvPort)
+{
+       edg_wlc_JobIdGetServerParts((edg_wlc_JobId) notifId, srvName, srvPort);
+}
+
+char* edg_wll_NotifIdGetUnique(const edg_wll_NotifId notifid)
+{
+       char       *id = edg_wlc_JobIdGetUnique((edg_wlc_JobId)notifid);
+
+       if ( id )
+       {
+               char *s = strchr(id, ':');
+
+               if ( s )
+               {
+               char *ret = strdup(s+1);
+                       free(id);
+                       return ret;
+               }
+       }
+
+       free(id);
+       return NULL;
+}
diff --git a/org.glite.lb.common/src/notifid.h b/org.glite.lb.common/src/notifid.h
new file mode 100644 (file)
index 0000000..fc52d01
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_NOTIFID_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_NOTIFID_H__
+
+#ident "$Header$"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Notification handle.
+ * Refers to a particular registration for receiving notifications.
+ */
+typedef void *edg_wll_NotifId;
+
+/** Parse and unparse the Id. */
+int edg_wll_NotifIdParse(const char *,edg_wll_NotifId *);
+char* edg_wll_NotifIdUnparse(const edg_wll_NotifId);
+
+int edg_wll_NotifIdCreate(const char *,int,edg_wll_NotifId *);
+void edg_wll_NotifIdFree(edg_wll_NotifId);
+
+void edg_wll_NotifIdGetServerParts(const edg_wll_NotifId, char **, unsigned int *);
+char *edg_wll_NotifIdGetUnique(const edg_wll_NotifId);
+int edg_wll_NotifIdSetUnique(edg_wll_NotifId *, const char *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/org.glite.lb.common/src/param.c b/org.glite.lb.common/src/param.c
new file mode 100644 (file)
index 0000000..479f654
--- /dev/null
@@ -0,0 +1,429 @@
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <errno.h>
+
+#include <globus_common.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/notification.h"
+#include "context-int.h"
+#include "log_proto.h"
+
+
+/* XXX: must match edg_wll_ContextParam */
+static const char *myenv[] = {
+       "GLOBUS_HOSTNAME",
+       NULL,
+       NULL,
+       NULL,
+       "EDG_WL_LOG_DESTINATION",
+       "EDG_WL_LOG_DESTINATION",
+       "EDG_WL_LOG_TIMEOUT",
+       "EDG_WL_LOG_SYNC_TIMEOUT",
+       "EDG_WL_QUERY_SERVER",
+       "EDG_WL_QUERY_SERVER",
+       "EDG_WL_QUERY_SERVER_OVERRIDE",
+       "EDG_WL_QUERY_TIMEOUT",
+       "EDG_WL_QUERY_JOBS_LIMIT",
+       "EDG_WL_QUERY_EVENTS_LIMIT",
+       "EDG_WL_QUERY_RESULTS",
+       "EDG_WL_QUERY_CONNECTIONS",
+       "EDG_WL_NOTIF_SERVER",
+       "EDG_WL_NOTIF_SERVER",
+       "EDG_WL_NOTIF_TIMEOUT",
+/* don't care about X509_USER_*, GSI looks at them anyway */
+       NULL,
+       NULL,
+       NULL,
+};
+
+/* XXX: does not parse URL, just hostname[:port] */
+
+static int extract_port(edg_wll_ContextParam param,int dflt)
+{
+       char    *p = NULL,*s = NULL;
+       if (myenv[param]) {
+               s = getenv(myenv[param]);
+               if (s) p = strchr(s,':');
+       }
+       return  p ? atoi(p+1) : dflt;
+}
+
+static int extract_num(edg_wll_ContextParam param,int dflt)
+{
+       if (myenv[param]) {
+               char *s = getenv(myenv[param]);
+               if (s) return(atoi(s));
+       }
+       return dflt;
+}
+
+static char *extract_host(edg_wll_ContextParam param,const char *dflt)
+{
+       char    *p,*s = NULL;
+
+       if (myenv[param]) s = getenv(myenv[param]);
+       if (!s && !dflt) return NULL;
+       s = strdup(s?s:dflt),
+       p = strchr(s,':');
+       if (p) *p = 0;
+       return s;
+}
+
+static void extract_time(edg_wll_ContextParam param,double dflt,struct timeval *t)
+{
+       char    *s = NULL;
+       double  d;
+
+       if (myenv[param]) s = getenv(myenv[param]);
+       d = s ? atof(s) : dflt;
+       t->tv_sec = (long) d;
+       t->tv_usec = (long) ((d-t->tv_sec)*1e6);
+}
+
+static char *extract_split(edg_wll_ContextParam param,char by,int index)
+{
+       int     i;
+       char    *s,*e;
+
+       if (!myenv[param]) return NULL;
+       if (!(s = getenv(myenv[param]))) return NULL;
+       for (i=0; i<index && (s=strchr(s,by));i++) s++;
+       return i==index ? ( (e = strchr(s,by)) ? strndup(s,e-s) : strdup(s))
+                       : NULL;
+}
+
+
+int edg_wll_SetParamString(edg_wll_Context ctx,edg_wll_ContextParam param,const char *val)
+{
+       char    hn[200];
+
+       switch (param) {
+               case EDG_WLL_PARAM_HOST:             
+                       globus_libc_gethostname(hn,sizeof hn);
+                       free(ctx->p_host);
+                       ctx->p_host = val ? strdup(val) : extract_host(param,hn);
+                       break;
+               case EDG_WLL_PARAM_INSTANCE:         
+                       free(ctx->p_instance);
+                       ctx->p_instance = val ? strdup(val) : extract_split(param,'/',1);
+                       break;
+               case EDG_WLL_PARAM_DESTINATION:      
+                       free(ctx->p_destination);
+                       ctx->p_destination = val ? strdup(val) : extract_host(param,EDG_WLL_LOG_HOST_DEFAULT);
+                       break;
+               case EDG_WLL_PARAM_QUERY_SERVER:     
+                       free(ctx->p_query_server);
+                       ctx->p_query_server = val ? strdup(val) : extract_host(param,NULL);
+                       break;
+               case EDG_WLL_PARAM_NOTIF_SERVER:     
+                       free(ctx->p_notif_server);
+                       ctx->p_notif_server = val ? strdup(val) : extract_host(param,NULL);
+                       break;
+               case EDG_WLL_PARAM_X509_PROXY:       
+                       free(ctx->p_proxy_filename);
+                       ctx->p_proxy_filename = val ? strdup(val) : NULL;
+                       break;
+               case EDG_WLL_PARAM_X509_KEY:         
+                       free(ctx->p_key_filename);
+                       ctx->p_key_filename = val ? strdup(val) : NULL;
+                       break;
+               case EDG_WLL_PARAM_X509_CERT:        
+                       free(ctx->p_cert_filename);
+                       ctx->p_cert_filename = val ? strdup(val) : NULL;
+                       break;
+               case EDG_WLL_PARAM_QUERY_SERVER_OVERRIDE:
+                       if (!val) val = getenv(myenv[param]);
+                       if (!val) val = "no";
+                       ctx->p_query_server_override = !strcasecmp(val,"yes");
+                       break;
+               default:
+                       return edg_wll_SetError(ctx,EINVAL,"unknown parameter");
+       }
+       return edg_wll_ResetError(ctx);
+}
+
+int edg_wll_SetParamInt(edg_wll_Context ctx,edg_wll_ContextParam param,int val)
+{
+       switch (param) {
+               case EDG_WLL_PARAM_LEVEL:
+                       ctx->p_level = val ? val : EDG_WLL_LEVEL_SYSTEM;
+                       break;
+               case EDG_WLL_PARAM_DESTINATION_PORT:
+                       ctx->p_dest_port = val ? val : extract_port(param,EDG_WLL_LOG_PORT_DEFAULT);
+                       break;
+               case EDG_WLL_PARAM_QUERY_SERVER_PORT:
+                       ctx->p_query_server_port = val ? val :
+                               extract_port(param,GLITE_WMSC_JOBID_DEFAULT_PORT);;
+                       break;
+               case EDG_WLL_PARAM_NOTIF_SERVER_PORT:
+                       ctx->p_notif_server_port = val ? val :
+                               extract_port(param,0);;
+                       // XXX: when default port is known, put it here
+                       break;
+               case EDG_WLL_PARAM_QUERY_JOBS_LIMIT:
+                       ctx->p_query_jobs_limit = val ? val :
+                               extract_num(param,0);
+                       break;
+               case EDG_WLL_PARAM_QUERY_EVENTS_LIMIT:
+                       ctx->p_query_events_limit = val ? val :
+                               extract_num(param,0);
+                       break;
+               case EDG_WLL_PARAM_QUERY_RESULTS:
+                       if (val) {
+                               if (val <= EDG_WLL_QUERYRES_UNDEF || val >= EDG_WLL_QUERYRES__LAST)
+                                       return edg_wll_SetError(ctx,EINVAL,"Query result parameter value out of range");
+
+                               ctx->p_query_results = val;
+                       }
+                       else {
+                               char    *s = extract_split(param,'/',0);
+                               if (s) {
+                                       val = edg_wll_StringToQResult(s);
+                                       if (!val) return edg_wll_SetError(ctx,EINVAL,"can't parse query result parameter name");
+                                       ctx->p_query_results = val;
+                                       free(s);
+                               }
+                               return edg_wll_SetError(ctx,EINVAL,"can't parse query result parameter name");
+                       }
+                       break;
+               case EDG_WLL_PARAM_QUERY_CONNECTIONS:
+                       {
+                               char *s = getenv(myenv[param]);
+                               
+                               if (!val && s) val = atoi(s);
+                               ctx->poolSize = val ? val : EDG_WLL_LOG_CONNECTIONS_DEFAULT;
+                       }
+                       break;
+               case EDG_WLL_PARAM_SOURCE:           
+                       if (val) {
+                               if (val <= EDG_WLL_SOURCE_NONE || val >= EDG_WLL_SOURCE__LAST)
+                                       return edg_wll_SetError(ctx,EINVAL,"Source out of range");
+
+                               ctx->p_source = val;
+                       }
+                       else {
+                               char    *s = extract_split(param,'/',0);
+                               if (s) {
+                                       val = edg_wll_StringToSource(s);
+                                       if (!val) return edg_wll_SetError(ctx,EINVAL,"can't parse source name");
+                                       ctx->p_source = val;
+                                       free(s);
+                               }
+                               return edg_wll_SetError(ctx,EINVAL,"can't parse source name");
+                       }
+                       break;
+               default:
+                       return edg_wll_SetError(ctx,EINVAL,"unknown parameter");
+       }
+       return edg_wll_ResetError(ctx);
+}
+
+int edg_wll_SetParamTime(edg_wll_Context ctx,edg_wll_ContextParam param,const struct timeval *val)
+{
+       switch (param) {
+               case EDG_WLL_PARAM_LOG_TIMEOUT:      
+/* XXX: check also if val is not grater than EDG_WLL_LOG_TIMEOUT_MAX */
+                       if (val) memcpy(&ctx->p_log_timeout,val,sizeof *val);
+                       else extract_time(param,EDG_WLL_LOG_TIMEOUT_DEFAULT,&ctx->p_log_timeout);
+                       break;
+               case EDG_WLL_PARAM_LOG_SYNC_TIMEOUT: 
+/* XXX: check also if val is not grater than EDG_WLL_LOG_SYNC_TIMEOUT_MAX */
+                       if (val) memcpy(&ctx->p_sync_timeout,val,sizeof *val);
+                       else extract_time(param,EDG_WLL_LOG_SYNC_TIMEOUT_DEFAULT,&ctx->p_sync_timeout);
+                       break;
+               case EDG_WLL_PARAM_QUERY_TIMEOUT:    
+/* XXX: check also if val is not grater than EDG_WLL_QUERY_TIMEOUT_MAX */
+                       if (val) memcpy(&ctx->p_query_timeout,val,sizeof *val);
+                       else extract_time(param,EDG_WLL_QUERY_TIMEOUT_DEFAULT,&ctx->p_query_timeout);
+                       break;
+               case EDG_WLL_PARAM_NOTIF_TIMEOUT:    
+/* XXX: check also if val is not grater than EDG_WLL_NOTIF_TIMEOUT_MAX */
+                       if (val) memcpy(&ctx->p_notif_timeout,val,sizeof *val);
+                       else extract_time(param,EDG_WLL_NOTIF_TIMEOUT_DEFAULT,&ctx->p_notif_timeout);
+                       break;
+               default:
+                       return edg_wll_SetError(ctx,EINVAL,"unknown parameter");
+       }
+       return edg_wll_ResetError(ctx);
+}
+
+int edg_wll_SetParam(edg_wll_Context ctx,edg_wll_ContextParam param,...)
+{
+       va_list ap;
+
+       va_start(ap,param);
+       switch (param) {
+               case EDG_WLL_PARAM_LEVEL:            
+               case EDG_WLL_PARAM_DESTINATION_PORT: 
+               case EDG_WLL_PARAM_QUERY_SERVER_PORT:
+               case EDG_WLL_PARAM_NOTIF_SERVER_PORT:
+               case EDG_WLL_PARAM_QUERY_JOBS_LIMIT:      
+               case EDG_WLL_PARAM_QUERY_EVENTS_LIMIT:      
+               case EDG_WLL_PARAM_QUERY_RESULTS:
+               case EDG_WLL_PARAM_QUERY_CONNECTIONS:
+               case EDG_WLL_PARAM_SOURCE:           
+                       return edg_wll_SetParamInt(ctx,param,va_arg(ap,int));
+               case EDG_WLL_PARAM_HOST:             
+               case EDG_WLL_PARAM_INSTANCE:         
+               case EDG_WLL_PARAM_DESTINATION:      
+               case EDG_WLL_PARAM_QUERY_SERVER:     
+               case EDG_WLL_PARAM_NOTIF_SERVER:     
+               case EDG_WLL_PARAM_QUERY_SERVER_OVERRIDE:
+               case EDG_WLL_PARAM_X509_PROXY:       
+               case EDG_WLL_PARAM_X509_KEY:         
+               case EDG_WLL_PARAM_X509_CERT:        
+                       return edg_wll_SetParamString(ctx,param,va_arg(ap,char *));
+               case EDG_WLL_PARAM_LOG_TIMEOUT:      
+               case EDG_WLL_PARAM_LOG_SYNC_TIMEOUT: 
+               case EDG_WLL_PARAM_QUERY_TIMEOUT:    
+               case EDG_WLL_PARAM_NOTIF_TIMEOUT:    
+                       return edg_wll_SetParamTime(ctx,param,va_arg(ap,struct timeval *));
+               default: 
+                       return edg_wll_SetError(ctx,EINVAL,"unknown parameter");
+       }
+}
+
+int edg_wll_GetParam(edg_wll_Context ctx,edg_wll_ContextParam param,...)
+{
+       va_list ap;
+       int     *p_int;
+       char    **p_string;
+       struct timeval  *p_tv;
+
+       edg_wll_ResetError(ctx);
+
+       va_start(ap,param);
+       switch (param) {
+               case EDG_WLL_PARAM_LEVEL:            
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_level;
+                       break;
+               case EDG_WLL_PARAM_DESTINATION_PORT: 
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_dest_port;
+                       break;
+               case EDG_WLL_PARAM_QUERY_SERVER_PORT:
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_query_server_port;
+                       break;
+               case EDG_WLL_PARAM_NOTIF_SERVER_PORT:
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_notif_server_port;
+                       break;
+               case EDG_WLL_PARAM_QUERY_JOBS_LIMIT:      
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_query_jobs_limit;
+                       break;
+               case EDG_WLL_PARAM_QUERY_EVENTS_LIMIT:      
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_query_events_limit;
+                       break;
+               case EDG_WLL_PARAM_QUERY_RESULTS:
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_query_results;
+                       break;
+               case EDG_WLL_PARAM_QUERY_CONNECTIONS:
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->poolSize;
+                       break;
+               case EDG_WLL_PARAM_SOURCE:           
+                       p_int = va_arg(ap, int *);
+                       *p_int = ctx->p_source;
+                       break;
+
+#define estrdup(x) ((x) ? strdup(x) : x)
+
+               case EDG_WLL_PARAM_HOST:             
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_host);
+                       break;
+               case EDG_WLL_PARAM_INSTANCE:         
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_instance);
+                       break;
+               case EDG_WLL_PARAM_DESTINATION:      
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_destination);
+                       break;
+               case EDG_WLL_PARAM_QUERY_SERVER:     
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_query_server);
+                       break;
+               case EDG_WLL_PARAM_NOTIF_SERVER:     
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_notif_server);
+                       break;
+               case EDG_WLL_PARAM_QUERY_SERVER_OVERRIDE:
+                       p_string = va_arg(ap, char **);
+                       *p_string = strdup(ctx->p_query_server_override ? "yes" : "no");
+                       break;
+               case EDG_WLL_PARAM_X509_PROXY:       
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_proxy_filename);
+                       break;
+               case EDG_WLL_PARAM_X509_KEY:         
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_key_filename);
+                       break;
+               case EDG_WLL_PARAM_X509_CERT:        
+                       p_string = va_arg(ap, char **);
+                       *p_string = estrdup(ctx->p_cert_filename);
+                       break;
+
+               case EDG_WLL_PARAM_LOG_TIMEOUT:      
+                       p_tv = va_arg(ap,struct timeval *);
+                       *p_tv = ctx->p_log_timeout;
+                       break;
+               case EDG_WLL_PARAM_LOG_SYNC_TIMEOUT: 
+                       p_tv = va_arg(ap,struct timeval *);
+                       *p_tv = ctx->p_sync_timeout;
+                       break;
+               case EDG_WLL_PARAM_QUERY_TIMEOUT:    
+                       p_tv = va_arg(ap,struct timeval *);
+                       *p_tv = ctx->p_query_timeout;
+                       break;
+               case EDG_WLL_PARAM_NOTIF_TIMEOUT:    
+                       p_tv = va_arg(ap,struct timeval *);
+                       *p_tv = ctx->p_notif_timeout;
+                       break;
+
+               default: 
+                       return edg_wll_SetError(ctx, EINVAL, "unknown parameter");
+                       break;
+       }
+       va_end(ap);
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+#if 0
+/* only for reference */
+edg_wll_ErrorCode edg_wll_SetLoggingParams(edg_wll_Context ctx, 
+                               const char      *jobid,
+                               const char      *service,
+                               const char      *hostname,
+                               const char      *instance,
+                               enum edg_wll_Level      level,
+                               const char      *proxy_filename,
+                               const char      *cert_filename,
+                               const char      *key_filename)
+{
+       edg_wll_ResetError(ctx);
+
+       if (jobid != NULL) ctx->p_jobid = strdup(jobid);
+       if (service != NULL) ctx->p_service = strdup(service);
+       if (hostname != NULL) ctx->p_hostname = strdup(hostname);
+       if (instance != NULL) ctx->p_instance = strdup(instance);
+
+        ctx->p_level = level;
+
+       if (proxy_filename != NULL) ctx->p_proxy_filename = strdup(proxy_filename);
+       if (cert_filename != NULL) ctx->p_cert_filename = strdup(cert_filename);
+       if (key_filename != NULL) ctx->p_key_filename = strdup(key_filename);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+#endif
diff --git a/org.glite.lb.common/src/query_rec.c b/org.glite.lb.common/src/query_rec.c
new file mode 100644 (file)
index 0000000..c57b8f6
--- /dev/null
@@ -0,0 +1,50 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+#include <assert.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/consumer.h"
+
+/*
+ * edg_wll_QueryRec manipulation routines
+ */
+
+void edg_wll_QueryRecFree(edg_wll_QueryRec *prec)
+{
+       if (prec == NULL) {
+               fprintf(stderr, "Error: edg_wll_QueryRecFree called with NULL parameter\n");
+               return;
+       }
+       switch (prec->attr) {
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+                       free(prec->attr_id.tag);
+               case EDG_WLL_QUERY_ATTR_OWNER: 
+               case EDG_WLL_QUERY_ATTR_LOCATION:
+               case EDG_WLL_QUERY_ATTR_DESTINATION:
+               case EDG_WLL_QUERY_ATTR_HOST:
+               case EDG_WLL_QUERY_ATTR_INSTANCE:
+                       if ( prec->value.c ) free(prec->value.c);
+                       break;
+               case EDG_WLL_QUERY_ATTR_JOBID:
+               case EDG_WLL_QUERY_ATTR_PARENT:
+                       edg_wlc_JobIdFree(prec->value.j);
+                       break;
+               case EDG_WLL_QUERY_ATTR_STATUS:
+               case EDG_WLL_QUERY_ATTR_DONECODE:
+               case EDG_WLL_QUERY_ATTR_LEVEL:
+               case EDG_WLL_QUERY_ATTR_SOURCE:
+               case EDG_WLL_QUERY_ATTR_EVENT_TYPE:
+               case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       /* do nothing */
+                       break;
+               default:
+                       fprintf(stderr,"Error(edg_wll_QueryRecFree): unknown edg_wll_QueryRec.attr=%d\n", prec->attr);
+                       break;
+       }
+}
+
diff --git a/org.glite.lb.common/src/status.c.T b/org.glite.lb.common/src/status.c.T
new file mode 100644 (file)
index 0000000..301c96b
--- /dev/null
@@ -0,0 +1,172 @@
+#ident "$Header$"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "glite/lb/consumer.h"
+
+static const struct timeval null_timeval = {0,0};
+
+
+void edg_wll_FreeStatus(edg_wll_JobStat *stat)
+{ 
+  if (stat) {
+       int     i;
+    
+@@@{
+        selectType $status '_common_';
+        for (getFieldsOrdered $status) {
+                my $f = selectField $status $_;
+                my $ft = $f->{type};
+                if ($ft eq 'jobid') {
+                        gen "\tedg_wlc_JobIdFree(stat->$_);\n";
+                }
+                if ($ft eq 'string') {
+                        gen "\tif (stat->$_ != NULL )    \tfree(stat->$_);\n";
+                }
+                if ($ft eq 'intlist') {
+                        gen "\tif (stat->$_ != NULL )    \tfree(stat->$_);\n";
+                }
+                if ($ft eq 'strlist') {
+                        gen "\tif (stat->$_ != NULL ) {\n";
+                       gen "\t\tfor (i=0; stat->$_\[i]; i++)\n";
+                       gen "\t\t\tfree(stat->$_\[i]);\n";
+                       gen "\t\tfree(stat->$_);\n";
+                       gen "\t}\n";
+                }
+                if ($ft eq 'taglist') {
+                        gen "\tif (stat->$_ != NULL ) {\n";
+                       gen "\t\tfor (i=0; stat->$_\[i].tag; i++) {\n";
+                       gen "\t\t\tfree(stat->$_\[i].tag);\n";
+                       gen "\t\t\tfree(stat->$_\[i].value);\n";
+                       gen "\t\t}\n";
+                       gen "\t\tfree(stat->$_);\n";
+                       gen "\t}\n";
+                }
+                if ($ft eq 'stslist') {
+                        gen "\tif (stat->$_ != NULL ) {\n";
+                       gen "\t\tfor (i=0; stat->$_\[i].state; i++)\n";
+                       gen "\t\t\tedg_wll_FreeStatus(&stat->$_\[i]);\n";
+                       gen "\t\tfree(stat->$_);\n";
+                       gen "\t}\n";
+                }
+
+        }
+@@@}
+    
+  }
+}
+
+edg_wll_JobStat *edg_wll_CpyStatus(const edg_wll_JobStat *src, edg_wll_JobStat *dest)
+{
+       int     i;
+
+
+       if ( !src || !dest )
+               return NULL;
+
+       edg_wll_InitStatus(dest);
+       dest->state = src->state;
+@@@{
+               selectType $status '_common_';
+               for (getFieldsOrdered $status) {
+                       my $f = selectField $status $_;
+                       my $ft = $f->{type};
+                       if ($ft eq 'jobid') {
+                               gen "\tif ( edg_wlc_JobIdDup(src->$_, &(dest->$_)) ) goto err;\n";
+                       }
+                       if ($ft eq 'string') {
+                               gen "\tif ( src->$_ != NULL )\n";
+                               gen "\t\tif ( !(dest->$_ = strdup(src->$_)) ) goto err;\n";
+                       }
+                       if ($ft eq 'intlist') {
+                               gen "\tif ( src->$_ != NULL )\n\t{\n";
+                               gen "\t\ti = 1 + src->$_\[0];\n";
+                               gen "\t\tif ( !(dest->$_ = malloc(sizeof(*src->$_)*i)) ) goto err;\n";
+                               gen "\t\tmemcpy(dest->$_,src->$_,sizeof(*src->$_)*i);\n\t}\n";
+                       }
+                       if ($ft eq 'strlist') {
+                               gen "\tif ( src->$_ != NULL )\n\t{\n";
+                               gen "\t\tfor ( i = 0; src->$_\[i]; i++ ) ;\n";
+                               gen "\t\tif ( !(dest->$_ = malloc(sizeof(*src->$_)*(i+1))) ) goto err;\n";
+                               gen "\t\tfor ( i = 0; src->$_\[i]; i++ )\n";
+                               gen "\t\t\tif ( !(dest->$_\[i] = strdup(src->$_\[i])) ) goto err;\n";
+                               gen "\t\tdest->$_\[i] = NULL;\n\t}\n";
+                       }
+                       if ($ft eq 'stslist') {
+                               gen "\tif ( src->$_ != NULL )\n\t{\n";
+                               gen "\t\tfor ( i = 0; src->$_\[i].state; i++ ) ;\n";
+                               gen "\t\tif ( !(dest->$_ = malloc(sizeof(*src->$_)*(i+1))) ) goto err;\n";
+                               gen "\t\tfor ( i = 0; src->$_\[i].state; i++ )\n";
+                               gen "\t\t\tif ( !edg_wll_CpyStatus(&src->$_\[i], &dest->$_\[i]) ) goto err;\n";
+                               gen "\t\tdest->$_\[i].state = EDG_WLL_JOB_UNDEF;\n\t}\n";
+                       }
+                       if (($ft eq 'bool') or ($ft eq 'timeval') or ($ft eq 'logsrc')
+                               or ($ft eq 'port') or ($ft eq 'level') or ($ft eq 'int')) {
+                               gen "\tdest->$_ = src->$_;\n";
+                       }
+                       if ($ft eq 'taglist') {
+                               gen qq{
+!      if (src->$_ != NULL) \{
+!              for (i=0; src->$_\[i].tag; i++);
+!              dest->$_ = malloc(sizeof(*src->$_)*(i+1));
+!              for (i=0; src->$_\[i].tag; i++) \{
+!                      dest->$_\[i].tag = strdup(src->$_\[i].tag);
+!                      dest->$_\[i].value = strdup(src->$_\[i].value);
+!              \}
+!              dest->$_\[i].tag = NULL;
+!        \}
+                               };
+                       }
+               }
+@@@}
+    
+  return dest;
+
+err:
+  edg_wll_FreeStatus(dest);
+  return NULL;
+}
+
+
+int edg_wll_InitStatus(edg_wll_JobStat *stat)
+{
+       if (!stat) return -1;
+       
+       stat->state = EDG_WLL_JOB_UNDEF;
+
+@@@{
+        selectType $status '_common_';
+        for (getFieldsOrdered $status) {
+                my $f = selectField $status $_;
+                gen "\tstat->$_ = $f->{null};\n"
+        }
+@@@}
+
+       return 0;
+}
+
+static const char * const statNames[] = {
+       "Undefined",
+@@@{
+        for (getTypesOrdered $status) {
+                gen "\t\"$_\",\n";
+        }
+@@@}
+};
+
+edg_wll_JobStatCode edg_wll_StringToStat(const char *name)
+
+{
+        unsigned int     i;
+
+        for (i=0; i<sizeof(statNames)/sizeof(statNames[0]); i++)
+                if (strcasecmp(statNames[i],name) == 0) return (edg_wll_JobStatCode) i;
+        return (edg_wll_JobStatCode) -1; 
+}
+
+char *edg_wll_StatToString(edg_wll_JobStatCode statCode)
+{
+        if ((int)statCode < 0 || statCode >= sizeof(statNames)/sizeof(statNames[0])) return (char *) NULL; 
+        return strdup(statNames[statCode]);
+}
diff --git a/org.glite.lb.common/src/strio.c b/org.glite.lb.common/src/strio.c
new file mode 100644 (file)
index 0000000..f1ab5b8
--- /dev/null
@@ -0,0 +1,581 @@
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ ************************************************************************/
+
+/*
+ * TODO
+ * - StrToLongDouble
+ */
+static const char rcsid[] = "@(#)$Id$";
+
+#if defined(unix) || defined(__xlC__) || defined(__QNX__)
+# define PLATFORM_UNIX
+#elif defined(WIN32) || defined(_WIN32)
+# define PLATFORM_WIN32
+#elif defined(AMIGA) && defined(__GNUC__)
+# define PLATFORM_UNIX
+#endif
+
+#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L)
+# define TRIO_C99
+#endif
+
+#include "strio.h"
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#include <stdarg.h>
+#include <time.h>
+#include <math.h>
+#ifndef DEBUG
+# define NDEBUG
+#endif
+#include <assert.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+#define NIL ((char)0)
+#ifndef FALSE
+# define FALSE (1 == 0)
+# define TRUE (! FALSE)
+#endif
+
+#define VALID(x) (NULL != (x))
+#define INVALID(x) (NULL == (x))
+
+#if defined(PLATFORM_UNIX)
+# define USE_STRCASECMP
+# define USE_STRNCASECMP
+# define USE_STRERROR
+# if defined(__QNX__)
+#  define strcasecmp(x,y) stricmp(x,y)
+#  define strncasecmp(x,y,n) strnicmp(x,y,n)
+# endif
+#elif defined(PLATFORM_WIN32)
+# define USE_STRCASECMP
+# define strcasecmp(x,y) strcmpi(x,y)
+#endif
+
+/*************************************************************************
+ * StrAppendMax
+ */
+char *StrAppendMax(char *target, size_t max, const char *source)
+{
+  assert(VALID(target));
+  assert(VALID(source));
+  assert(max > 0);
+
+  max -= StrLength(target) + 1;
+  return (max > 0) ? strncat(target, source, max) : target;
+}
+
+/*************************************************************************
+ * StrCopyMax
+ */
+char *StrCopyMax(char *target, size_t max, const char *source)
+{
+  assert(VALID(target));
+  assert(VALID(source));
+  assert(max > 0); /* Includes != 0 */
+
+  target = strncpy(target, source, max - 1);
+  target[max - 1] = (char)0;
+  return target;
+}
+
+/*************************************************************************
+ * StrDuplicate
+ */
+char *StrDuplicate(const char *source)
+{
+  char *target;
+
+  assert(VALID(source));
+
+  target = StrAlloc(StrLength(source) + 1);
+  if (target)
+    {
+      StrCopy(target, source);
+    }
+  return target;
+}
+
+/*************************************************************************
+ * StrDuplicateMax
+ */
+char *StrDuplicateMax(const char *source, size_t max)
+{
+  char *target;
+  size_t len;
+
+  assert(VALID(source));
+  assert(max > 0);
+
+  /* Make room for string plus a terminating zero */
+  len = StrLength(source) + 1;
+  if (len > max)
+    {
+      len = max;
+    }
+  target = StrAlloc(len);
+  if (target)
+    {
+      StrCopyMax(target, len, source);
+    }
+  return target;
+}
+
+/*************************************************************************
+ * StrEqual
+ */
+int StrEqual(const char *first, const char *second)
+{
+  assert(VALID(first));
+  assert(VALID(second));
+
+  if (VALID(first) && VALID(second))
+    {
+#if defined(USE_STRCASECMP)
+      return (0 == strcasecmp(first, second));
+#else
+      while ((*first != NIL) && (*second != NIL))
+       {
+         if (toupper(*first) != toupper(*second))
+           {
+             break;
+           }
+         first++;
+         second++;
+       }
+      return ((*first == NIL) && (*second == NIL));
+#endif
+    }
+  return FALSE;
+}
+
+/*************************************************************************
+ * StrEqualCase
+ */
+int StrEqualCase(const char *first, const char *second)
+{
+  assert(VALID(first));
+  assert(VALID(second));
+
+  if (VALID(first) && VALID(second))
+    {
+      return (0 == strcmp(first, second));
+    }
+  return FALSE;
+}
+
+/*************************************************************************
+ * StrEqualCaseMax
+ */
+int StrEqualCaseMax(const char *first, size_t max, const char *second)
+{
+  assert(VALID(first));
+  assert(VALID(second));
+
+  if (VALID(first) && VALID(second))
+    {
+      return (0 == strncmp(first, second, max));
+    }
+  return FALSE;
+}
+
+/*************************************************************************
+ * StrEqualLocale
+ */
+int StrEqualLocale(const char *first, const char *second)
+{
+  assert(VALID(first));
+  assert(VALID(second));
+
+#if defined(LC_COLLATE)
+  return (strcoll(first, second) == 0);
+#else
+  return StrEqual(first, second);
+#endif
+}
+
+/*************************************************************************
+ * StrEqualMax
+ */
+int StrEqualMax(const char *first, size_t max, const char *second)
+{
+  assert(VALID(first));
+  assert(VALID(second));
+
+  if (VALID(first) && VALID(second))
+    {
+#if defined(USE_STRNCASECMP)
+      return (0 == strncasecmp(first, second, max));
+#else
+      /* Not adequately tested yet */
+      size_t cnt = 0;
+      while ((*first != NIL) && (*second != NIL) && (cnt <= max))
+       {
+         if (toupper(*first) != toupper(*second))
+           {
+             break;
+           }
+         first++;
+         second++;
+         cnt++;
+       }
+      return ((cnt == max) || ((*first == NIL) && (*second == NIL)));
+#endif
+    }
+  return FALSE;
+}
+
+/*************************************************************************
+ * StrError
+ */
+const char *StrError(int errorNumber)
+{
+#if defined(USE_STRERROR)
+  return strerror(errorNumber);
+#else
+  return "unknown";
+#endif
+}
+
+/*************************************************************************
+ * StrFormatDate
+ */
+size_t StrFormatDateMax(char *target,
+                       size_t max,
+                       const char *format,
+                       const struct tm *datetime)
+{
+  assert(VALID(target));
+  assert(VALID(format));
+  assert(VALID(datetime));
+  assert(max > 0);
+  
+  return strftime(target, max, format, datetime);
+}
+
+/*************************************************************************
+ * StrHash
+ */
+unsigned long StrHash(const char *string, int type)
+{
+  unsigned long value = 0L;
+  char ch;
+
+  assert(VALID(string));
+  
+  switch (type)
+    {
+    case STRIO_HASH_PLAIN:
+      while ( (ch = *string++) != NIL )
+       {
+         value *= 31;
+         value += (unsigned long)ch;
+       }
+      break;
+    default:
+      assert(FALSE);
+      break;
+    }
+  return value;
+}
+
+/*************************************************************************
+ * StrMatch
+ */
+int StrMatch(char *string, char *pattern)
+{
+  assert(VALID(string));
+  assert(VALID(pattern));
+  
+  for (; ('*' != *pattern); ++pattern, ++string)
+    {
+      if (NIL == *string)
+       {
+         return (NIL == *pattern);
+       }
+      if ((toupper((int)*string) != toupper((int)*pattern))
+         && ('?' != *pattern))
+       {
+         return FALSE;
+       }
+    }
+  /* two-line patch to prevent *too* much recursiveness: */
+  while ('*' == pattern[1])
+    pattern++;
+
+  do
+    {
+      if ( StrMatch(string, &pattern[1]) )
+       {
+         return TRUE;
+       }
+    }
+  while (*string++);
+  
+  return FALSE;
+}
+
+/*************************************************************************
+ * StrMatchCase
+ */
+int StrMatchCase(char *string, char *pattern)
+{
+  assert(VALID(string));
+  assert(VALID(pattern));
+  
+  for (; ('*' != *pattern); ++pattern, ++string)
+    {
+      if (NIL == *string)
+       {
+         return (NIL == *pattern);
+       }
+      if ((*string != *pattern)
+         && ('?' != *pattern))
+       {
+         return FALSE;
+       }
+    }
+  /* two-line patch to prevent *too* much recursiveness: */
+  while ('*' == pattern[1])
+    pattern++;
+
+  do
+    {
+      if ( StrMatchCase(string, &pattern[1]) )
+       {
+         return TRUE;
+       }
+    }
+  while (*string++);
+  
+  return FALSE;
+}
+
+/*************************************************************************
+ * StrSpanFunction
+ *
+ * Untested
+ */
+size_t StrSpanFunction(char *source, int (*Function)(int))
+{
+  size_t count = 0;
+
+  assert(VALID(source));
+  assert(VALID(Function));
+  
+  while (*source != NIL)
+    {
+      if (Function(*source))
+       break; /* while */
+      source++;
+      count++;
+    }
+  return count;
+}
+
+/*************************************************************************
+ * StrSubstringMax
+ */
+char *StrSubstringMax(const char *string, size_t max, const char *find)
+{
+  size_t count;
+  size_t size;
+  char *result = NULL;
+
+  assert(VALID(string));
+  assert(VALID(find));
+  
+  size = StrLength(find);
+  if (size <= max)
+    {
+      for (count = 0; count <= max - size; count++)
+       {
+         if (StrEqualMax(find, size, &string[count]))
+           {
+             result = (char *)&string[count];
+             break;
+           }
+       }
+    }
+  return result;
+}
+
+/*************************************************************************
+ * StrToDouble
+ *
+ * double ::= [ <sign> ]
+ *            ( <number> |
+ *              <number> <decimal_point> <number> |
+ *              <decimal_point> <number> )
+ *            [ <exponential> [ <sign> ] <number> ]
+ * number ::= 1*( <digit> )
+ * digit ::= ( '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' )
+ * exponential ::= ( 'e' | 'E' )
+ * sign ::= ( '-' | '+' )
+ * decimal_point ::= '.'
+ */
+double StrToDouble(const char *source, const char **endp)
+{
+#if defined(TRIO_C99)
+  return strtod(source, endp);
+#else
+  /* Preliminary code */
+  int isNegative = FALSE;
+  int isExponentNegative = FALSE;
+  unsigned long integer = 0;
+  unsigned long fraction = 0;
+  unsigned long fracdiv = 1;
+  unsigned long exponent = 0;
+  double value = 0.0;
+
+  /* First try hex-floats */
+  if ((source[0] == '0') && ((source[1] == 'x') || (source[1] == 'X')))
+    {
+      source += 2;
+      while (isxdigit((int)*source))
+       {
+         integer *= 16;
+         integer += (isdigit((int)*source)
+                     ? (*source - '0')
+                     : 10 + (toupper((int)*source) - 'A'));
+         source++;
+       }
+      if (*source == '.')
+       {
+         source++;
+         while (isxdigit((int)*source))
+           {
+             fraction *= 16;
+             fraction += (isdigit((int)*source)
+                          ? (*source - '0')
+                          : 10 + (toupper((int)*source) - 'A'));
+             fracdiv *= 16;
+             source++;
+           }
+         if ((*source == 'p') || (*source == 'P'))
+           {
+             source++;
+             if ((*source == '+') || (*source == '-'))
+               {
+                 isExponentNegative = (*source == '-');
+                 source++;
+               }
+             while (isdigit((int)*source))
+               {
+                 exponent *= 10;
+                 exponent += (*source - '0');
+                 source++;
+               }
+           }
+       }
+    }
+  else /* Then try normal decimal floats */
+    {
+      isNegative = (*source == '-');
+      /* Skip sign */
+      if ((*source == '+') || (*source == '-'))
+       source++;
+
+      /* Integer part */
+      while (isdigit((int)*source))
+       {
+         integer *= 10;
+         integer += (*source - '0');
+         source++;
+       }
+
+      if (*source == '.')
+       {
+         source++; /* skip decimal point */
+         while (isdigit((int)*source))
+           {
+             fraction *= 10;
+             fraction += (*source - '0');
+             fracdiv *= 10;
+             source++;
+           }
+       }
+      if ((*source == 'e') || (*source == 'E'))
+       {
+         source++; /* Skip exponential indicator */
+         isExponentNegative = (*source == '-');
+         if ((*source == '+') || (*source == '-'))
+           source++;
+         while (isdigit((int)*source))
+           {
+             exponent *= 10;
+             exponent += (*source - '0');
+             source++;
+           }
+       }
+    }
+  
+  value = (double)integer;
+  if (fraction != 0)
+    {
+      value += (double)fraction / (double)fracdiv;
+    }
+  if (exponent != 0)
+    {
+      if (isExponentNegative)
+       value /= pow((double)10, (double)exponent);
+      else
+       value *= pow((double)10, (double)exponent);
+    }
+  if (isNegative)
+    value = -value;
+
+  if (endp)
+    *endp = source;
+  return value;
+#endif
+}
+
+/*************************************************************************
+ * StrToFloat
+ */
+float StrToFloat(const char *source, const char **endp)
+{
+#if defined(TRIO_C99)
+  return strtof(source, endp);
+#else
+  return (float)StrToDouble(source, endp);
+#endif
+}
+
+/*************************************************************************
+ * StrToUpper
+ */
+int StrToUpper(char *target)
+{
+  int i = 0;
+
+  assert(VALID(target));
+  
+  while (NIL != *target)
+    {
+      *target = toupper((int)*target);
+      target++;
+      i++;
+    }
+  return i;
+}
diff --git a/org.glite.lb.common/src/strio.h b/org.glite.lb.common/src/strio.h
new file mode 100644 (file)
index 0000000..68845a3
--- /dev/null
@@ -0,0 +1,227 @@
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ ************************************************************************/
+
+#ifndef TRIO_STRIO_H
+#define TRIO_STRIO_H
+
+#if !(defined(DEBUG) || defined(NDEBUG))
+# define NDEBUG
+#endif
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifndef STRIO_MALLOC
+# define STRIO_MALLOC(n) malloc(n)
+#endif
+#ifndef STRIO_FREE
+# define STRIO_FREE(x) free(x)
+#endif
+
+/*
+ * StrAppend(target, source)
+ * StrAppendMax(target, maxsize, source)
+ *
+ *   Append 'source' to 'target'
+ *
+ * target = StrAlloc(size)
+ *
+ *   Allocate a new string
+ *
+ * StrContains(target, substring)
+ *
+ *   Find out if the string 'substring' is
+ *   contained in the string 'target'
+ *
+ * StrCopy(target, source)
+ * StrCopyMax(target, maxsize, source)
+ *
+ *   Copy 'source' to 'target'
+ *
+ * target = StrDuplicate(source)
+ * target = StrDuplicateMax(source, maxsize)
+ *
+ *   Allocate and copy 'source' to 'target'
+ *
+ * StrEqual(first, second)
+ * StrEqualMax(first, maxsize, second)
+ *
+ *   Compare if 'first' is equal to 'second'.
+ *   Case-independent.
+ *
+ * StrEqualCase(first, second)
+ * StrEqualCaseMax(first, maxsize, second)
+ *
+ *   Compare if 'first' is equal to 'second'
+ *   Case-dependent. Please note that the use of the
+ *   word 'case' has the opposite meaning as that of
+ *   strcasecmp().
+ *
+ * StrFormat(target, format, ...)
+ * StrFormatMax(target, maxsize, format, ...)
+ *
+ *   Build 'target' according to 'format' and succesive
+ *   arguments. This is equal to the sprintf() and
+ *   snprintf() functions.
+ *
+ * StrFormatDate(target, format, ...)
+ *
+ * StrFree(target)
+ *
+ *   De-allocates a string
+ *
+ * StrHash(string, type)
+ *
+ *   Calculates the hash value of 'string' based on the
+ *   'type'.
+ *
+ * StrIndex(target, character)
+ * StrIndexLast(target, character)
+ *
+ *   Find the first/last occurrence of 'character' in
+ *   'target'
+ *
+ * StrLength(target)
+ *
+ *   Return the length of 'target'
+ *
+ * StrMatch(string, pattern)
+ * StrMatchCase(string, pattern)
+ *
+ *   Find 'pattern' within 'string'. 'pattern' may contain
+ *   wildcards such as * (asterics) and ? (question mark)
+ *   which matches zero or more characters and exactly
+ *   on character respectively
+ *
+ * StrScan(source, format, ...)
+ *
+ *   Equal to sscanf()
+ *
+ * StrSubstring(target, substring)
+ *
+ *   Find the first occurrence of the string 'substring'
+ *   within the string 'target'
+ *
+ * StrTokenize(target, list)
+ *
+ *   Split 'target' into the first token delimited by
+ *   one of the characters in 'list'. If 'target' is
+ *   NULL then next token will be returned.
+ *
+ * StrToUpper(target)
+ *
+ *   Convert all lower case characters in 'target' into
+ *   upper case characters.
+ */
+
+enum {
+  STRIO_HASH_NONE = 0,
+  STRIO_HASH_PLAIN,
+  STRIO_HASH_TWOSIGNED
+};
+
+#if !defined(DEBUG) || defined(__DECC)
+#define StrAlloc(n) (char *)STRIO_MALLOC(n)
+#define StrAppend(x,y) strcat((x), (y))
+#define StrContains(x,y) (0 != strstr((x), (y)))
+#define StrCopy(x,y) strcpy((x), (y))
+#define StrIndex(x,y) strchr((x), (y))
+#define StrIndexLast(x,y) strrchr((x), (y))
+#define StrFree(x) STRIO_FREE(x)
+#define StrLength(x) strlen((x))
+#define StrSubstring(x,y) strstr((x), (y))
+#define StrTokenize(x,y) strtok((x), (y))
+#define StrToLong(x,y,n) strtol((x), (y), (n))
+#define StrToUnsignedLong(x,y,n) strtoul((x), (y), (n))
+#else /* DEBUG */
+ /*
+  * To be able to use these macros everywhere, including in
+  * if() sentences, the assertions are put first in a comma
+  * seperated list.
+  *
+  * Unfortunately the DECC compiler does not seem to like this
+  * so it will use the un-asserted functions above for the
+  * debugging case too.
+  */
+#define StrAlloc(n) \
+     (assert((n) > 0),\
+      (char *)STRIO_MALLOC(n))
+#define StrAppend(x,y) \
+     (assert((x) != NULL),\
+      assert((y) != NULL),\
+      strcat((x), (y)))
+#define StrContains(x,y) \
+     (assert((x) != NULL),\
+      assert((y) != NULL),\
+      (0 != strstr((x), (y))))
+#define StrCopy(x,y) \
+     (assert((x) != NULL),\
+      assert((y) != NULL),\
+      strcpy((x), (y)))
+#define StrIndex(x,c) \
+     (assert((x) != NULL),\
+      strchr((x), (c)))
+#define StrIndexLast(x,c) \
+     (assert((x) != NULL),\
+      strrchr((x), (c)))
+#define StrFree(x) \
+     (assert((x) != NULL),\
+      STRIO_FREE(x))
+#define StrLength(x) \
+     (assert((x) != NULL),\
+      strlen((x)))
+#define StrSubstring(x,y) \
+     (assert((x) != NULL),\
+      assert((y) != NULL),\
+      strstr((x), (y)))
+#define StrTokenize(x,y) \
+     (assert((y) != NULL),\
+      strtok((x), (y)))
+#define StrToLong(x,y,n) \
+     (assert((x) != NULL),\
+      assert((y) != NULL),\
+      assert((n) >= 2 && (n) <= 36),\
+      strtol((x), (y), (n)))
+#define StrToUnsignedLong(x,y,n) \
+     (assert((x) != NULL),\
+      assert((y) != NULL),\
+      assert((n) >= 2 && (n) <= 36),\
+      strtoul((x), (y), (n)))
+#endif /* DEBUG */
+
+char *StrAppendMax(char *target, size_t max, const char *source);
+char *StrCopyMax(char *target, size_t max, const char *source);
+char *StrDuplicate(const char *source);
+char *StrDuplicateMax(const char *source, size_t max);
+int StrEqual(const char *first, const char *second);
+int StrEqualCase(const char *first, const char *second);
+int StrEqualCaseMax(const char *first, size_t max, const char *second);
+int StrEqualLocale(const char *first, const char *second);
+int StrEqualMax(const char *first, size_t max, const char *second);
+const char *StrError(int);
+size_t StrFormatDateMax(char *target, size_t max, const char *format, const struct tm *datetime);
+unsigned long StrHash(const char *string, int type);
+int StrMatch(char *string, char *pattern);
+int StrMatchCase(char *string, char *pattern);
+size_t StrSpanFunction(char *source, int (*Function)(int));
+char *StrSubstringMax(const char *string, size_t max, const char *find);
+float StrToFloat(const char *source, const char **target);
+double StrToDouble(const char *source, const char **target);
+int StrToUpper(char *target);
+
+#endif /* TRIO_STRIO_H */
diff --git a/org.glite.lb.common/src/trio.c b/org.glite.lb.common/src/trio.c
new file mode 100644 (file)
index 0000000..d46736e
--- /dev/null
@@ -0,0 +1,5708 @@
+
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 1998 Bjorn Reese and Daniel Stenberg.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ *************************************************************************
+ *
+ * A note to trio contributors:
+ *
+ * Avoid heap allocation at all costs to ensure that the trio functions
+ * are async-safe. The exceptions are the printf/fprintf functions, which
+ * uses fputc, and the asprintf functions and the <alloc> modifier, which
+ * by design are required to allocate form the heap.
+ *
+ ************************************************************************/
+
+/*
+ * TODO:
+ *  - Scan is probably too permissive about its modifiers.
+ *  - C escapes in %#[] ?
+ *  - C99 support has not been properly tested.
+ *  - Multibyte characters (done for format parsing, except scan groups)
+ *  - Complex numbers? (C99 _Complex)
+ *  - Boolean values? (C99 _Bool)
+ *  - C99 NaN(n-char-sequence) missing
+ *  - Should we support the GNU %a alloc modifier? GNU has an ugly hack
+ *    for %a, because C99 used %a for other purposes. If specified as
+ *    %as or %a[ it is interpreted as the alloc modifier, otherwise as
+ *    the C99 hex-float. This means that you cannot scan %as as a hex-float
+ *    immediately followed by an 's'.
+ *  - Scanning of collating symbols.
+ */
+
+static const char rcsid[] = "@(#)$Id$";
+
+/*************************************************************************
+ * Trio include files
+ */
+#include "trio.h"
+#include "triop.h"
+#include "strio.h"
+
+#ifdef DATAGRID_EXTENSION
+#include "glite/lb/events.h"
+#include "escape.h"
+//#include "edg/workload/thirdparty/trio/events.h"
+#endif
+
+/*
+ * Encode the error code and the position. This is decoded
+ * with TRIO_ERROR_CODE and TRIO_ERROR_POSITION.
+ */
+#if TRIO_ERRORS
+# define TRIO_ERROR_RETURN(x,y) (- ((x) + ((y) << 8)))
+#else
+# define TRIO_ERROR_RETURN(x,y) (-1)
+#endif
+
+
+/*************************************************************************
+ * Platform and compiler support detection
+ */
+#if defined(unix) || defined(__xlC__) || defined(_AIX) || defined(__QNX__)
+# define PLATFORM_UNIX
+#elif defined(AMIGA) && defined(__GNUC__)
+# define PLATFORM_UNIX
+#elif defined(WIN32) || defined(_WIN32) || defined(_MSC_VER)
+# define PLATFORM_WIN32
+# define TRIO_MSVC_5 1100
+#endif
+
+#if defined(__STDC__) && defined(__STDC_VERSION__)
+# if (__STDC_VERSION__ >= 199409L)
+#  define TRIO_COMPILER_SUPPORTS_ISO94
+# endif
+# if (__STDC_VERSION__ >= 199901L)
+#  define TRIO_COMPILER_SUPPORTS_C99
+# endif
+#endif
+
+#if defined(_XOPEN_SOURCE) && defined(_XOPEN_SOURCE_EXTENDED)
+# define TRIO_COMPILER_SUPPORTS_UNIX98
+#endif
+
+#if defined(__STDC_ISO_10646__) || defined(MB_LEN_MAX) || defined(USE_MULTIBYTE) || TRIO_WIDECHAR
+# define TRIO_COMPILER_SUPPORTS_MULTIBYTE
+# if !defined(MB_LEN_MAX)
+#  define MB_LEN_MAX 6
+# endif
+#endif
+
+
+/*************************************************************************
+ * Generic definitions
+ */
+
+#if !(defined(DEBUG) || defined(NDEBUG))
+# define NDEBUG
+#endif
+#include <assert.h>
+#include <ctype.h>
+#if !defined(TRIO_COMPILER_SUPPORTS_C99) && !defined(isblank)
+# define isblank(x) (((x)==32) || ((x)==9))
+#endif
+#include <math.h>
+#include <limits.h>
+#include <float.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <errno.h>
+
+#ifndef NULL
+# define NULL 0
+#endif
+#define NIL ((char)0)
+#ifndef FALSE
+# define FALSE (1 == 0)
+# define TRUE (! FALSE)
+#endif
+#define BOOLEAN_T int
+
+/* mincore() can be used for debugging purposes */
+#define VALID(x) (NULL != (x))
+
+/* xlC crashes on log10(0) */
+#define guarded_log10(x) (((x) == 0.0) ? -HUGE_VAL : log10(x))
+#define guarded_log16(x) (guarded_log10(x) / log10(16.0))
+
+
+/*************************************************************************
+ * Platform specific definitions
+ */
+#if defined(PLATFORM_UNIX)
+# include <unistd.h>
+# include <signal.h>
+# include <locale.h>
+# define USE_LOCALE
+#endif /* PLATFORM_UNIX */
+#if defined(PLATFORM_WIN32)
+# include <io.h>
+# define read _read
+# define write _write
+#endif /* PLATFORM_WIN32 */
+
+#if TRIO_WIDECHAR
+# if defined(TRIO_COMPILER_SUPPORTS_ISO94)
+#  include <wchar.h>
+#  include <wctype.h>
+# else
+typedef char wchar_t;
+typedef int wint_t;
+#  define WEOF EOF
+#  define iswalnum(x) isalnum(x)
+#  define iswalpha(x) isalpha(x)
+#  define iswblank(x) isblank(x)
+#  define iswcntrl(x) iscntrl(x)
+#  define iswdigit(x) isdigit(x)
+#  define iswgraph(x) isgraph(x)
+#  define iswlower(x) islower(x)
+#  define iswprint(x) isprint(x)
+#  define iswpunct(x) ispunct(x)
+#  define iswspace(x) isspace(x)
+#  define iswupper(x) isupper(x)
+#  define iswxdigit(x) isxdigit(x)
+# endif
+#endif
+
+
+/*************************************************************************
+ * Compiler dependent definitions
+ */
+
+/* Support for long long */
+#ifndef __cplusplus
+# if !defined(USE_LONGLONG)
+#  if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#   define USE_LONGLONG
+#  elif defined(__SUNPRO_C)
+#   define USE_LONGLONG
+#  elif defined(_LONG_LONG) || defined(_LONGLONG)
+#   define USE_LONGLONG
+#  endif
+# endif
+#endif
+
+/* The extra long numbers */
+#if defined(USE_LONGLONG)
+typedef signed long long int trio_longlong_t;
+typedef unsigned long long int trio_ulonglong_t;
+#elif defined(_MSC_VER)
+# if (_MSC_VER >= TRIO_MSVC_5)
+typedef signed __int64 trio_longlong_t;
+typedef unsigned __int64 trio_ulonglong_t;
+# else
+typedef signed long int trio_longlong_t;
+typedef unsigned long int trio_ulonglong_t;
+# endif
+#else
+typedef signed long int trio_longlong_t;
+typedef unsigned long int trio_ulonglong_t;
+#endif
+
+/* Maximal and fixed integer types */
+#if defined(TRIO_COMPILER_SUPPORTS_C99)
+# include <stdint.h>
+typedef intmax_t trio_intmax_t;
+typedef uintmax_t trio_uintmax_t;
+typedef int8_t trio_int8_t;
+typedef int16_t trio_int16_t;
+typedef int32_t trio_int32_t;
+typedef int64_t trio_int64_t;
+#elif defined(TRIO_COMPILER_SUPPORTS_UNIX98)
+# include <inttypes.h>
+typedef intmax_t trio_intmax_t;
+typedef uintmax_t trio_uintmax_t;
+typedef int8_t trio_int8_t;
+typedef int16_t trio_int16_t;
+typedef int32_t trio_int32_t;
+typedef int64_t trio_int64_t;
+#elif defined(_MSC_VER) && (_MSC_VER >= TRIO_MSVC_5)
+typedef trio_longlong_t trio_intmax_t;
+typedef trio_ulonglong_t trio_uintmax_t;
+typedef __int8 trio_int8_t;
+typedef __int16 trio_int16_t;
+typedef __int32 trio_int32_t;
+typedef __int64 trio_int64_t;
+#else
+typedef trio_longlong_t trio_intmax_t;
+typedef trio_ulonglong_t trio_uintmax_t;
+# if defined(TRIO_INT8_T)
+typedef TRIO_INT8_T trio_int8_t;
+# else
+typedef signed char trio_int8_t;
+# endif
+# if defined(TRIO_INT16_T)
+typedef TRIO_INT16_T trio_int16_t;
+# else
+typedef signed short trio_int16_t;
+# endif
+# if defined(TRIO_INT32_T)
+typedef TRIO_INT32_T trio_int32_t;
+# else
+typedef signed int trio_int32_t;
+# endif
+# if defined(TRIO_INT64_T)
+typedef TRIO_INT64_T trio_int64_t;
+# else
+typedef trio_longlong_t trio_int64_t;
+# endif
+#endif
+
+
+/*************************************************************************
+ * Internal definitions
+ */
+
+/* Long double sizes */
+#ifdef LDBL_DIG
+# define MAX_MANTISSA_DIGITS LDBL_DIG
+# define MAX_EXPONENT_DIGITS 4
+#else
+# define MAX_MANTISSA_DIGITS DBL_DIG
+# define MAX_EXPONENT_DIGITS 3
+#endif
+
+/* The maximal number of digits is for base 2 */
+#define MAX_CHARS_IN(x) (sizeof(x) * CHAR_BIT)
+/* The width of a pointer. The number of bits in a hex digit is 4 */
+#define POINTER_WIDTH ((sizeof("0x") - 1) + sizeof(void *) * CHAR_BIT / 4)
+
+/* Infinite and Not-A-Number for floating-point */
+#define INFINITE_LOWER "inf"
+#define INFINITE_UPPER "INF"
+#define LONG_INFINITE_LOWER "infinite"
+#define LONG_INFINITE_UPPER "INFINITE"
+#define NAN_LOWER "nan"
+#define NAN_UPPER "NAN"
+
+/* Various constants */
+enum {
+  TYPE_PRINT = 1,
+  TYPE_SCAN  = 2,
+
+  /* Flags. Use maximum 32 */
+  FLAGS_NEW                 = 0,
+  FLAGS_STICKY              = 1,
+  FLAGS_SPACE               = 2 * FLAGS_STICKY,
+  FLAGS_SHOWSIGN            = 2 * FLAGS_SPACE,
+  FLAGS_LEFTADJUST          = 2 * FLAGS_SHOWSIGN,
+  FLAGS_ALTERNATIVE         = 2 * FLAGS_LEFTADJUST,
+  FLAGS_SHORT               = 2 * FLAGS_ALTERNATIVE,
+  FLAGS_SHORTSHORT          = 2 * FLAGS_SHORT,
+  FLAGS_LONG                = 2 * FLAGS_SHORTSHORT,
+  FLAGS_QUAD                = 2 * FLAGS_LONG,
+  FLAGS_LONGDOUBLE          = 2 * FLAGS_QUAD,
+  FLAGS_SIZE_T              = 2 * FLAGS_LONGDOUBLE,
+  FLAGS_PTRDIFF_T           = 2 * FLAGS_SIZE_T,
+  FLAGS_INTMAX_T            = 2 * FLAGS_PTRDIFF_T,
+  FLAGS_NILPADDING          = 2 * FLAGS_INTMAX_T,
+  FLAGS_UNSIGNED            = 2 * FLAGS_NILPADDING,
+  FLAGS_UPPER               = 2 * FLAGS_UNSIGNED,
+  FLAGS_WIDTH               = 2 * FLAGS_UPPER,
+  FLAGS_WIDTH_PARAMETER     = 2 * FLAGS_WIDTH,
+  FLAGS_PRECISION           = 2 * FLAGS_WIDTH_PARAMETER,
+  FLAGS_PRECISION_PARAMETER = 2 * FLAGS_PRECISION,
+  FLAGS_BASE                = 2 * FLAGS_PRECISION_PARAMETER,
+  FLAGS_BASE_PARAMETER      = 2 * FLAGS_BASE,
+  FLAGS_FLOAT_E             = 2 * FLAGS_BASE_PARAMETER,
+  FLAGS_FLOAT_G             = 2 * FLAGS_FLOAT_E,
+  FLAGS_QUOTE               = 2 * FLAGS_FLOAT_G,
+  FLAGS_WIDECHAR            = 2 * FLAGS_QUOTE,
+  FLAGS_ALLOC               = 2 * FLAGS_WIDECHAR,
+  FLAGS_IGNORE              = 2 * FLAGS_ALLOC,
+  FLAGS_IGNORE_PARAMETER    = 2 * FLAGS_IGNORE,
+  FLAGS_VARSIZE_PARAMETER   = 2 * FLAGS_IGNORE_PARAMETER,
+  FLAGS_FIXED_SIZE          = 2 * FLAGS_VARSIZE_PARAMETER,
+  /* Reused flags */
+  FLAGS_EXCLUDE             = FLAGS_SHORT,
+  FLAGS_USER_DEFINED        = FLAGS_IGNORE,
+  /* Compounded flags */
+  FLAGS_ALL_VARSIZES        = FLAGS_LONG | FLAGS_QUAD | FLAGS_INTMAX_T | FLAGS_PTRDIFF_T | FLAGS_SIZE_T,
+  FLAGS_ALL_SIZES           = FLAGS_ALL_VARSIZES | FLAGS_SHORTSHORT | FLAGS_SHORT,
+
+  NO_POSITION  = -1,
+  NO_WIDTH     =  0,
+  NO_PRECISION = -1,
+  NO_SIZE      = -1,
+
+  NO_BASE      = -1,
+  MIN_BASE     =  2,
+  MAX_BASE     = 36,
+  BASE_BINARY  =  2,
+  BASE_OCTAL   =  8,
+  BASE_DECIMAL = 10,
+  BASE_HEX     = 16,
+
+  /* Maximal number of allowed parameters */
+  MAX_PARAMETERS = 64,
+  /* Maximal number of characters in class */
+  MAX_CHARACTER_CLASS = UCHAR_MAX,
+
+  /* Maximal string lengths for user-defined specifiers */
+  MAX_USER_NAME = 64,
+  MAX_USER_DATA = 256,
+  
+  /* Maximal length of locale separator strings */
+  MAX_LOCALE_SEPARATOR_LENGTH = MB_LEN_MAX,
+  /* Maximal number of integers in grouping */
+  MAX_LOCALE_GROUPS = 64
+};
+
+#define NO_GROUPING ((int)CHAR_MAX)
+
+/* Fundamental formatting parameter types */
+#define FORMAT_UNKNOWN   0
+#define FORMAT_INT       1
+#define FORMAT_DOUBLE    2
+#define FORMAT_CHAR      3
+#define FORMAT_STRING    4
+#define FORMAT_POINTER   5
+#define FORMAT_COUNT     6
+#define FORMAT_PARAMETER 7
+#define FORMAT_GROUP     8
+#if TRIO_GNU
+# define FORMAT_ERRNO    9
+#endif
+#if TRIO_EXTENSION
+# define FORMAT_USER_DEFINED 10
+#endif
+
+/* Character constants */
+#define CHAR_IDENTIFIER '%'
+#define CHAR_BACKSLASH '\\'
+#define CHAR_QUOTE '\"'
+#define CHAR_ADJUST ' '
+
+/* Character class expressions */
+#define CLASS_ALNUM ":alnum:"
+#define CLASS_ALPHA ":alpha:"
+#define CLASS_CNTRL ":cntrl:"
+#define CLASS_DIGIT ":digit:"
+#define CLASS_GRAPH ":graph:"
+#define CLASS_LOWER ":lower:"
+#define CLASS_PRINT ":print:"
+#define CLASS_PUNCT ":punct:"
+#define CLASS_SPACE ":space:"
+#define CLASS_UPPER ":upper:"
+#define CLASS_XDIGIT ":xdigit:"
+
+/*
+ * SPECIFIERS:
+ *
+ *
+ * a  Hex-float
+ * A  Hex-float
+ * c  Character
+ * C  Widechar character (wint_t)
+ * d  Decimal
+ * e  Float
+ * E  Float
+ * F  Float
+ * F  Float
+ * g  Float
+ * G  Float
+ * i  Integer
+ * m  Error message
+ * n  Count
+ * o  Octal
+ * p  Pointer
+ * s  String
+ * S  Widechar string (wchar_t *)
+ * u  Unsigned
+ * x  Hex
+ * X  Hex
+ * [] Group
+ * <> User-defined
+ *
+ * Reserved:
+ *
+ * D  Binary Coded Decimal %D(length,precision) (OS/390)
+ */
+#define SPECIFIER_CHAR 'c'
+#define SPECIFIER_STRING 's'
+#define SPECIFIER_DECIMAL 'd'
+#define SPECIFIER_INTEGER 'i'
+#define SPECIFIER_UNSIGNED 'u'
+#define SPECIFIER_OCTAL 'o'
+#define SPECIFIER_HEX 'x'
+#define SPECIFIER_HEX_UPPER 'X'
+#define SPECIFIER_FLOAT_E 'e'
+#define SPECIFIER_FLOAT_E_UPPER 'E'
+#define SPECIFIER_FLOAT_F 'f'
+#define SPECIFIER_FLOAT_F_UPPER 'F'
+#define SPECIFIER_FLOAT_G 'g'
+#define SPECIFIER_FLOAT_G_UPPER 'G'
+#define SPECIFIER_POINTER 'p'
+#define SPECIFIER_GROUP '['
+#define SPECIFIER_UNGROUP ']'
+#define SPECIFIER_COUNT 'n'
+#if TRIO_UNIX98
+# define SPECIFIER_CHAR_UPPER 'C'
+# define SPECIFIER_STRING_UPPER 'S'
+#endif
+#if TRIO_C99
+# define SPECIFIER_HEXFLOAT 'a'
+# define SPECIFIER_HEXFLOAT_UPPER 'A'
+#endif
+#if TRIO_GNU
+# define SPECIFIER_ERRNO 'm'
+#endif
+#if TRIO_EXTENSION
+# define SPECIFIER_BINARY 'b'
+# define SPECIFIER_BINARY_UPPER 'B'
+# define SPECIFIER_USER_DEFINED_BEGIN '<'
+# define SPECIFIER_USER_DEFINED_END '>'
+# define SPECIFIER_USER_DEFINED_SEPARATOR ':'
+#endif
+
+/*
+ * QUALIFIERS:
+ *
+ *
+ * Numbers = d,i,o,u,x,X
+ * Float = a,A,e,E,f,F,g,G
+ * String = s
+ * Char = c
+ *
+ *
+ * 9$ Position
+ *      Use the 9th parameter. 9 can be any number between 1 and
+ *      the maximal argument
+ *
+ * 9 Width
+ *      Set width to 9. 9 can be any number, but must not be postfixed
+ *      by '$'
+ *
+ * h  Short
+ *    Numbers:
+ *      (unsigned) short int
+ *
+ * hh Short short
+ *    Numbers:
+ *      (unsigned) char
+ *
+ * l  Long
+ *    Numbers:
+ *      (unsigned) long int
+ *    String:
+ *      as the S specifier
+ *    Char:
+ *      as the C specifier
+ *
+ * ll Long Long
+ *    Numbers:
+ *      (unsigned) long long int
+ *
+ * L  Long Double
+ *    Float
+ *      long double
+ *
+ * #  Alternative
+ *    Float:
+ *      Decimal-point is always present
+ *    String:
+ *      non-printable characters are handled as \number
+ *
+ *    Spacing
+ *
+ * +  Sign
+ *
+ * -  Alignment
+ *
+ * .  Precision
+ *
+ * *  Parameter
+ *    print: use parameter
+ *    scan: no parameter (ignore)
+ *
+ * q  Quad
+ *
+ * Z  size_t
+ *
+ * w  Widechar
+ *
+ * '  Thousands/quote
+ *    Numbers:
+ *      Integer part grouped in thousands
+ *    Binary numbers:
+ *      Number grouped in nibbles (4 bits)
+ *    String:
+ *      Quoted string
+ *
+ * j  intmax_t
+ * t  prtdiff_t
+ * z  size_t
+ *
+ * !  Sticky
+ * @  Parameter (for both print and scan)
+ *
+ * I  n-bit Integer
+ *    Numbers:
+ *      The following options exists
+ *        I8  = 8-bit integer
+ *        I16 = 16-bit integer
+ *        I32 = 32-bit integer
+ *        I64 = 64-bit integer
+ */
+#define QUALIFIER_POSITION '$'
+#define QUALIFIER_SHORT 'h'
+#define QUALIFIER_LONG 'l'
+#define QUALIFIER_LONG_UPPER 'L'
+#define QUALIFIER_ALTERNATIVE '#'
+#define QUALIFIER_SPACE ' '
+#define QUALIFIER_PLUS '+'
+#define QUALIFIER_MINUS '-'
+#define QUALIFIER_DOT '.'
+#define QUALIFIER_STAR '*'
+#define QUALIFIER_CIRCUMFLEX '^'
+#if TRIO_C99
+# define QUALIFIER_SIZE_T 'z'
+# define QUALIFIER_PTRDIFF_T 't'
+# define QUALIFIER_INTMAX_T 'j'
+#endif
+#if TRIO_BSD || TRIO_GNU
+# define QUALIFIER_QUAD 'q'
+#endif
+#if TRIO_GNU
+# define QUALIFIER_SIZE_T_UPPER 'Z'
+#endif
+#if TRIO_MISC
+# define QUALIFIER_WIDECHAR 'w'
+#endif
+#if TRIO_MICROSOFT
+# define QUALIFIER_FIXED_SIZE 'I'
+#endif
+#if TRIO_EXTENSION
+# define QUALIFIER_QUOTE '\''
+# define QUALIFIER_STICKY '!'
+# define QUALIFIER_VARSIZE '&' /* This should remain undocumented */
+# define QUALIFIER_PARAM '@' /* Experimental */
+# define QUALIFIER_COLON ':' /* For scanlists */
+# define QUALIFIER_EQUAL '=' /* For scanlists */
+#endif
+#if DATAGRID_EXTENSION
+# define QUALIFIER_ESCAPE '|'
+#endif
+
+
+/*************************************************************************
+ * Internal structures
+ */
+
+/* Parameters */
+typedef struct {
+  int type;
+  unsigned long flags;
+  int width;
+  int precision;
+  int base;
+  int varsize;
+#ifdef QUALIFIER_ESCAPE
+  enum dg_escape { ESCAPE_NONE, ESCAPE_ULM, ESCAPE_XML, ESCAPE_SQL } escape;
+#endif
+  int indexAfterSpecifier;
+  union {
+    char *string;
+#if TRIO_WIDECHAR
+    wchar_t *wstring;
+#endif
+    void *pointer;
+    union {
+      trio_uintmax_t as_signed;
+      trio_intmax_t as_unsigned;
+    } number;
+    double doubleNumber;
+    double *doublePointer;
+    long double longdoubleNumber;
+    long double *longdoublePointer;
+    int errorNumber;
+  } data;
+  /* For the user-defined specifier */
+  char user_name[MAX_USER_NAME];
+  char user_data[MAX_USER_DATA];
+} parameter_T;
+
+/* General trio "class" */
+typedef struct _trio_T {
+  void *location;
+  void (*OutStream)(struct _trio_T *, int);
+  void (*InStream)(struct _trio_T *, int *);
+  /*
+   * The number of characters that would have been written/read if
+   * there had been sufficient space.
+   */
+  int processed;
+  /*
+   * The number of characters that are actually written/read.
+   * Processed and committed with only differ for the *nprintf
+   * and *nscanf functions.
+   */
+  int committed;
+  int max;
+  int current;
+} trio_T;
+
+/* References (for user-defined callbacks) */
+typedef struct _reference_T {
+  trio_T *data;
+  parameter_T *parameter;
+} reference_T;
+
+/* Registered entries (for user-defined callbacks) */
+typedef struct _userdef_T {
+  struct _userdef_T *next;
+  trio_callback_t callback;
+  char *name;
+} userdef_T;
+
+
+/*************************************************************************
+ * Internal variables
+ */
+
+static const char null[] = "(nil)";
+
+#if defined(USE_LOCALE)
+static struct lconv *internalLocaleValues = NULL;
+#endif
+
+/*
+ * UNIX98 says "in a locale where the radix character is not defined,
+ * the radix character defaults to a period (.)"
+ */
+static char internalDecimalPoint[MAX_LOCALE_SEPARATOR_LENGTH + 1] = ".";
+static char internalThousandSeparator[MAX_LOCALE_SEPARATOR_LENGTH + 1] = ",";
+static char internalGrouping[MAX_LOCALE_GROUPS] = { (char)NO_GROUPING };
+
+static const char internalDigitsLower[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+static const char internalDigitsUpper[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+static BOOLEAN_T internalDigitsUnconverted = TRUE;
+static int internalDigitArray[128];
+#if TRIO_EXTENSION
+static BOOLEAN_T internalCollationUnconverted = TRUE;
+static char internalCollationArray[MAX_CHARACTER_CLASS][MAX_CHARACTER_CLASS];
+#endif
+
+static volatile trio_callback_t internalEnterCriticalRegion = NULL;
+static volatile trio_callback_t internalLeaveCriticalRegion = NULL;
+static userdef_T *internalUserDef = NULL;
+
+
+/*************************************************************************
+ * trio_strerror [public]
+ */
+const char *trio_strerror(int errorcode)
+{
+  /* Textual versions of the error codes */
+  switch (TRIO_ERROR_CODE(errorcode))
+    {
+    case TRIO_EOF:
+      return "End of file";
+    case TRIO_EINVAL:
+      return "Invalid argument";
+    case TRIO_ETOOMANY:
+      return "Too many arguments";
+    case TRIO_EDBLREF:
+      return "Double reference";
+    case TRIO_EGAP:
+      return "Reference gap";
+    case TRIO_ENOMEM:
+      return "Out of memory";
+    case TRIO_ERANGE:
+      return "Invalid range";
+    default:
+      return "Unknown";
+    }
+}
+
+/*************************************************************************
+ * TrioIsQualifier [private]
+ *
+ * Description:
+ *  Remember to add all new qualifiers to this function.
+ *  QUALIFIER_POSITION must not be added.
+ */
+static BOOLEAN_T
+TrioIsQualifier(const char ch)
+{
+  /* QUALIFIER_POSITION is not included */
+  switch (ch)
+    {
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9':
+    case QUALIFIER_PLUS:
+    case QUALIFIER_MINUS:
+    case QUALIFIER_SPACE:
+    case QUALIFIER_DOT:
+    case QUALIFIER_STAR:
+    case QUALIFIER_ALTERNATIVE:
+    case QUALIFIER_SHORT:
+    case QUALIFIER_LONG:
+    case QUALIFIER_LONG_UPPER:
+    case QUALIFIER_CIRCUMFLEX:
+#if defined(QUALIFIER_SIZE_T)
+    case QUALIFIER_SIZE_T:
+#endif
+#if defined(QUALIFIER_PTRDIFF_T)
+    case QUALIFIER_PTRDIFF_T:
+#endif
+#if defined(QUALIFIER_INTMAX_T)
+    case QUALIFIER_INTMAX_T:
+#endif
+#if defined(QUALIFIER_QUAD)
+    case QUALIFIER_QUAD:
+#endif
+#if defined(QUALIFIER_SIZE_T_UPPER)
+    case QUALIFIER_SIZE_T_UPPER:
+#endif
+#if defined(QUALIFIER_WIDECHAR)
+    case QUALIFIER_WIDECHAR:
+#endif
+#if defined(QUALIFIER_QUOTE)
+    case QUALIFIER_QUOTE:
+#endif
+#if defined(QUALIFIER_STICKY)
+    case QUALIFIER_STICKY:
+#endif
+#if defined(QUALIFIER_VARSIZE)
+    case QUALIFIER_VARSIZE:
+#endif
+#if defined(QUALIFIER_PARAM)
+    case QUALIFIER_PARAM:
+#endif
+#if defined(QUALIFIER_FIXED_SIZE)
+    case QUALIFIER_FIXED_SIZE:
+#endif
+#ifdef QUALIFIER_ESCAPE
+    case QUALIFIER_ESCAPE:
+#endif
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+/*************************************************************************
+ * TrioGenerateNan [private]
+ *
+ * Calculating NaN portably is difficult. Some compilers will emit
+ * warnings about divide by zero, and others will simply fail to
+ * generate a NaN.
+ */
+static double
+TrioGenerateNaN(void)
+{
+#if defined(TRIO_COMPILER_SUPPORTS_C99)
+  return nan(NULL);
+#elif defined(DBL_QNAN)
+  return DBL_QNAN;
+#elif defined(PLATFORM_UNIX)
+  double value;
+  void (*signal_handler)(int);
+  
+  signal_handler = signal(SIGFPE, SIG_IGN);
+  value = 0.0 / 0.0;
+  signal(SIGFPE, signal_handler);
+  return value;
+#else
+  return 0.0 / 0.0;
+#endif
+}
+
+/*************************************************************************
+ * TrioIsNan [private]
+ */
+static int
+TrioIsNan(double number)
+{
+#ifdef isnan
+  /* C99 defines isnan() as a macro */
+  return isnan(number);
+#else
+  double integral, fraction;
+  
+  return (/* NaN is the only number which does not compare to itself */
+         (number != number) ||
+         /* Fallback solution if NaN compares to NaN */
+         ((number != 0.0) &&
+          (fraction = modf(number, &integral),
+           integral == fraction)));
+#endif
+}
+
+/*************************************************************************
+ * TrioIsInfinite [private]
+ */
+static int
+TrioIsInfinite(double number)
+{
+#ifdef isinf
+  /* C99 defines isinf() as a macro */
+  return isinf(number);
+#else
+  return ((number == HUGE_VAL) ? 1 : ((number == -HUGE_VAL) ? -1 : 0));
+#endif
+}
+
+/*************************************************************************
+ * TrioSetLocale [private]
+ */
+#if defined(USE_LOCALE)
+static void
+TrioSetLocale(void)
+{
+  internalLocaleValues = (struct lconv *)localeconv();
+  if (internalLocaleValues)
+    {
+      if ((internalLocaleValues->decimal_point) &&
+         (internalLocaleValues->decimal_point[0] != NIL))
+       {
+         StrCopyMax(internalDecimalPoint,
+                    sizeof(internalDecimalPoint),
+                    internalLocaleValues->decimal_point);
+       }
+      if ((internalLocaleValues->thousands_sep) &&
+         (internalLocaleValues->thousands_sep[0] != NIL))
+       {
+         StrCopyMax(internalThousandSeparator,
+                    sizeof(internalThousandSeparator),
+                    internalLocaleValues->thousands_sep);
+       }
+      if ((internalLocaleValues->grouping) &&
+         (internalLocaleValues->grouping[0] != NIL))
+       {
+         StrCopyMax(internalGrouping,
+                    sizeof(internalGrouping),
+                    internalLocaleValues->grouping);
+       }
+    }
+}
+#endif /* defined(USE_LOCALE) */
+
+/*************************************************************************
+ * TrioGetPosition [private]
+ *
+ * Get the %n$ position.
+ */
+static int
+TrioGetPosition(const char *format,
+               int *indexPointer)
+{
+  char *tmpformat;
+  int number = 0;
+  int index = *indexPointer;
+
+  number = (int)StrToLong(&format[index], &tmpformat, BASE_DECIMAL);
+  index = (int)(tmpformat - format);
+  if ((number != 0) && (QUALIFIER_POSITION == format[index++]))
+    {
+      *indexPointer = index;
+      /*
+       * number is decreased by 1, because n$ starts from 1, whereas
+       * the array it is indexing starts from 0.
+       */
+      return number - 1;
+    }
+  return NO_POSITION;
+}
+
+/*************************************************************************
+ * TrioFindNamespace [private]
+ *
+ * Find registered user-defined specifier.
+ * The prev argument is used for optimisation only.
+ */
+static userdef_T *
+TrioFindNamespace(const char *name, userdef_T **prev)
+{
+  userdef_T *def;
+  
+  if (internalEnterCriticalRegion)
+    (void)internalEnterCriticalRegion(NULL);
+  
+  for (def = internalUserDef; def; def = def->next)
+    {
+      /* Case-sensitive string comparison */
+      if (StrEqualCase(def->name, name))
+       break;
+      
+      if (prev)
+       *prev = def;
+    }
+  
+  if (internalLeaveCriticalRegion)
+    (void)internalLeaveCriticalRegion(NULL);
+  
+  return def;
+}
+
+/*************************************************************************
+ * TrioPreprocess [private]
+ *
+ * Description:
+ *  Parse the format string
+ */
+static int
+TrioPreprocess(int type,
+              const char *format,
+              parameter_T *parameters,
+              va_list arglist,
+              void **argarray)
+{
+#if TRIO_ERRORS
+  /* Count the number of times a parameter is referenced */
+  unsigned short usedEntries[MAX_PARAMETERS];
+#endif
+  /* Parameter counters */
+  int parameterPosition;
+  int currentParam;
+  int maxParam = -1;
+  /* Utility variables */
+  unsigned long flags;
+  int width;
+  int precision;
+  int varsize;
+#ifdef QUALIFIER_ESCAPE
+  enum dg_escape escape;
+#endif
+  int base;
+  int index;  /* Index into formatting string */
+  int dots;  /* Count number of dots in modifier part */
+  BOOLEAN_T positional;  /* Does the specifier have a positional? */
+  BOOLEAN_T got_sticky = FALSE;  /* Are there any sticky modifiers at all? */
+  /*
+   * indices specifies the order in which the parameters must be
+   * read from the va_args (this is necessary to handle positionals)
+   */
+  int indices[MAX_PARAMETERS];
+  int pos = 0;
+  /* Various variables */
+  char ch;
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  int charlen;
+#endif
+  int i = -1;
+  int num;
+  char *tmpformat;
+
+
+#if TRIO_ERRORS
+  /*
+   * The 'parameters' array is not initialized, but we need to
+   * know which entries we have used.
+   */
+  memset(usedEntries, 0, sizeof(usedEntries));
+#endif
+
+  index = 0;
+  parameterPosition = 0;
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  mblen(NULL, 0);
+#endif
+  
+  while (format[index])
+    {
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+      if (! isascii(format[index]))
+       {
+         /*
+          * Multibyte characters cannot be legal specifiers or
+          * modifiers, so we skip over them.
+          */
+         charlen = mblen(&format[index], MB_LEN_MAX);
+         index += (charlen > 0) ? charlen : 1;
+         continue; /* while */
+       }
+#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */
+      if (CHAR_IDENTIFIER == format[index++])
+       {
+         if (CHAR_IDENTIFIER == format[index])
+           {
+             index++;
+             continue; /* while */
+           }
+
+         flags = FLAGS_NEW;
+         dots = 0;
+         currentParam = TrioGetPosition(format, &index);
+         positional = (NO_POSITION != currentParam);
+         if (!positional)
+           {
+             /* We have no positional, get the next counter */
+             currentParam = parameterPosition;
+           }
+          if(currentParam >= MAX_PARAMETERS)
+           {
+             /* Bail out completely to make the error more obvious */
+             return TRIO_ERROR_RETURN(TRIO_ETOOMANY, index);
+           }
+
+         if (currentParam > maxParam)
+           maxParam = currentParam;
+
+         /* Default values */
+         width = NO_WIDTH;
+         precision = NO_PRECISION;
+         base = NO_BASE;
+         varsize = NO_SIZE;
+#ifdef QUALIFIER_ESCAPE
+         escape = ESCAPE_NONE;
+#endif
+
+         while (TrioIsQualifier(format[index]))
+           {
+             ch = format[index++];
+
+             switch (ch)
+               {
+               case QUALIFIER_SPACE:
+                 flags |= FLAGS_SPACE;
+                 break;
+
+               case QUALIFIER_PLUS:
+                 flags |= FLAGS_SHOWSIGN;
+                 break;
+
+               case QUALIFIER_MINUS:
+                 flags |= FLAGS_LEFTADJUST;
+                 flags &= ~FLAGS_NILPADDING;
+                 break;
+
+               case QUALIFIER_ALTERNATIVE:
+                 flags |= FLAGS_ALTERNATIVE;
+                 break;
+
+               case QUALIFIER_DOT:
+                 if (dots == 0) /* Precision */
+                   {
+                     dots++;
+
+                     /* Skip if no precision */
+                     if (QUALIFIER_DOT == format[index])
+                       break;
+                     
+                     /* After the first dot we have the precision */
+                     flags |= FLAGS_PRECISION;
+                     if ((QUALIFIER_STAR == format[index]) ||
+                         (QUALIFIER_PARAM == format[index]))
+                       {
+                         index++;
+                         flags |= FLAGS_PRECISION_PARAMETER;
+
+                         precision = TrioGetPosition(format, &index);
+                         if (precision == NO_POSITION)
+                           {
+                             parameterPosition++;
+                             if (positional)
+                               precision = parameterPosition;
+                             else
+                               {
+                                 precision = currentParam;
+                                 currentParam = precision + 1;
+                               }
+                           }
+                         else
+                           {
+                             if (! positional)
+                               currentParam = precision + 1;
+                             if (width > maxParam)
+                               maxParam = precision;
+                           }
+                         if (currentParam > maxParam)
+                           maxParam = currentParam;
+                       }
+                     else
+                       {
+                         precision = StrToLong(&format[index], &tmpformat, BASE_DECIMAL);
+                         index = (int)(tmpformat - format);
+                       }
+                   }
+                 else if (dots == 1) /* Base */
+                   {
+                     dots++;
+                     
+                     /* After the second dot we have the base */
+                     flags |= FLAGS_BASE;
+                     if ((QUALIFIER_STAR == format[index]) ||
+                         (QUALIFIER_PARAM == format[index]))
+                       {
+                         index++;
+                         flags |= FLAGS_BASE_PARAMETER;
+                         base = TrioGetPosition(format, &index);
+                         if (base == NO_POSITION)
+                           {
+                             parameterPosition++;
+                             if (positional)
+                               base = parameterPosition;
+                             else
+                               {
+                                 base = currentParam;
+                                 currentParam = base + 1;
+                               }
+                           }
+                         else
+                           {
+                             if (! positional)
+                               currentParam = base + 1;
+                             if (base > maxParam)
+                               maxParam = base;
+                           }
+                         if (currentParam > maxParam)
+                           maxParam = currentParam;
+                       }
+                     else
+                       {
+                         base = StrToLong(&format[index], &tmpformat, BASE_DECIMAL);
+                         if (base > MAX_BASE)
+                           return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+                         index = (int)(tmpformat - format);
+                       }
+                   }
+                 else
+                   {
+                     return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+                   }
+                 break; /* QUALIFIER_DOT */
+
+               case QUALIFIER_PARAM:
+                 type = TYPE_PRINT;
+                 /* FALLTHROUGH */
+               case QUALIFIER_STAR:
+                 /* This has different meanings for print and scan */
+                 if (TYPE_PRINT == type)
+                   {
+                     /* Read with from parameter */
+                     flags |= (FLAGS_WIDTH | FLAGS_WIDTH_PARAMETER);
+                     width = TrioGetPosition(format, &index);
+                     if (width == NO_POSITION)
+                       {
+                         parameterPosition++;
+                         if (positional)
+                           width = parameterPosition;
+                         else
+                           {
+                             width = currentParam;
+                             currentParam = width + 1;
+                           }
+                       }
+                     else
+                       {
+                         if (! positional)
+                           currentParam = width + 1;
+                         if (width > maxParam)
+                           maxParam = width;
+                       }
+                     if (currentParam > maxParam)
+                       maxParam = currentParam;
+                   }
+                 else
+                   {
+                     /* Scan, but do not store result */
+                     flags |= FLAGS_IGNORE;
+                   }
+
+                 break; /* QUALIFIER_STAR */
+
+               case '0':
+                 if (! (flags & FLAGS_LEFTADJUST))
+                   flags |= FLAGS_NILPADDING;
+                 /* FALLTHROUGH */
+               case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                 flags |= FLAGS_WIDTH;
+                 /* &format[index - 1] is used to "rewind" the read
+                  * character from format
+                  */
+                 width = StrToLong(&format[index - 1], &tmpformat, BASE_DECIMAL);
+                 index = (int)(tmpformat - format);
+                 break;
+
+               case QUALIFIER_SHORT:
+                 if (flags & FLAGS_SHORTSHORT)
+                   return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+                 else if (flags & FLAGS_SHORT)
+                   flags |= FLAGS_SHORTSHORT;
+                 else
+                   flags |= FLAGS_SHORT;
+                 break;
+
+               case QUALIFIER_LONG:
+                 if (flags & FLAGS_QUAD)
+                   return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+                 else if (flags & FLAGS_LONG)
+                   flags |= FLAGS_QUAD;
+                 else
+                   flags |= FLAGS_LONG;
+                 break;
+
+               case QUALIFIER_LONG_UPPER:
+                 flags |= FLAGS_LONGDOUBLE;
+                 break;
+
+#if defined(QUALIFIER_SIZE_T)
+               case QUALIFIER_SIZE_T:
+                 flags |= FLAGS_SIZE_T;
+                 /* Modify flags for later truncation of number */
+                 if (sizeof(size_t) == sizeof(trio_ulonglong_t))
+                   flags |= FLAGS_QUAD;
+                 else if (sizeof(size_t) == sizeof(long))
+                   flags |= FLAGS_LONG;
+                 break;
+#endif
+
+#if defined(QUALIFIER_PTRDIFF_T)
+               case QUALIFIER_PTRDIFF_T:
+                 flags |= FLAGS_PTRDIFF_T;
+                 if (sizeof(ptrdiff_t) == sizeof(trio_ulonglong_t))
+                   flags |= FLAGS_QUAD;
+                 else if (sizeof(ptrdiff_t) == sizeof(long))
+                   flags |= FLAGS_LONG;
+                 break;
+#endif
+
+#if defined(QUALIFIER_INTMAX_T)
+               case QUALIFIER_INTMAX_T:
+                 flags |= FLAGS_INTMAX_T;
+                 if (sizeof(trio_intmax_t) == sizeof(trio_ulonglong_t))
+                   flags |= FLAGS_QUAD;
+                 else if (sizeof(trio_intmax_t) == sizeof(long))
+                   flags |= FLAGS_LONG;
+                 break;
+#endif
+
+#if defined(QUALIFIER_QUAD)
+               case QUALIFIER_QUAD:
+                 flags |= FLAGS_QUAD;
+                 break;
+#endif
+
+#if defined(QUALIFIER_FIXED_SIZE)
+               case QUALIFIER_FIXED_SIZE:
+                 if (flags & FLAGS_FIXED_SIZE)
+                   return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+
+                 if (flags & (FLAGS_ALL_SIZES | FLAGS_LONGDOUBLE |
+                              FLAGS_WIDECHAR | FLAGS_VARSIZE_PARAMETER))
+                   return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+
+                 if ((format[index] == '6') &&
+                     (format[index + 1] == '4'))
+                   {
+                     varsize = sizeof(trio_int64_t);
+                     index += 2;
+                   }
+                 else if ((format[index] == '3') &&
+                          (format[index + 1] == '2'))
+                   {
+                     varsize = sizeof(trio_int32_t);
+                     index += 2;
+                   }
+                 else if ((format[index] == '1') &&
+                          (format[index + 1] == '6'))
+                   {
+                     varsize = sizeof(trio_int16_t);
+                     index += 2;
+                   }
+                 else if (format[index] == '8')
+                   {
+                     varsize = sizeof(trio_int8_t);
+                     index++;
+                   }
+                 else
+                   return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+                 
+                 flags |= FLAGS_FIXED_SIZE;
+                 break;
+#endif
+
+#ifdef QUALIFIER_ESCAPE
+               case QUALIFIER_ESCAPE:
+                 switch (format[index++]) {
+                   case 'U': escape = ESCAPE_ULM; break;
+                   case 'X': escape = ESCAPE_XML; break;
+                   case 'S': escape = ESCAPE_SQL; break;
+                   default: return TRIO_ERROR_RETURN(TRIO_EINVAL,index);
+                 }
+                 break;
+#endif
+
+
+#if defined(QUALIFIER_WIDECHAR)
+               case QUALIFIER_WIDECHAR:
+                 flags |= FLAGS_WIDECHAR;
+                 break;
+#endif
+
+#if defined(QUALIFIER_SIZE_T_UPPER)
+               case QUALIFIER_SIZE_T_UPPER:
+                 break;
+#endif
+
+#if defined(QUALIFIER_QUOTE)
+               case QUALIFIER_QUOTE:
+                 flags |= FLAGS_QUOTE;
+                 break;
+#endif
+
+#if defined(QUALIFIER_STICKY)
+               case QUALIFIER_STICKY:
+                 flags |= FLAGS_STICKY;
+                 got_sticky = TRUE;
+                 break;
+#endif
+                 
+#if defined(QUALIFIER_VARSIZE)
+               case QUALIFIER_VARSIZE:
+                 flags |= FLAGS_VARSIZE_PARAMETER;
+                 parameterPosition++;
+                 if (positional)
+                   varsize = parameterPosition;
+                 else
+                   {
+                     varsize = currentParam;
+                     currentParam = varsize + 1;
+                   }
+                 if (currentParam > maxParam)
+                   maxParam = currentParam;
+                 break;
+#endif
+
+               default:
+                 /* Bail out completely to make the error more obvious */
+                  return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+               }
+           } /* while qualifier */
+
+         /*
+          * Parameters only need the type and value. The value is
+          * read later.
+          */
+         if (flags & FLAGS_WIDTH_PARAMETER)
+           {
+#if TRIO_ERRORS
+             usedEntries[width] += 1;
+#endif
+             parameters[pos].type = FORMAT_PARAMETER;
+             indices[width] = pos;
+             width = pos++;
+           }
+         if (flags & FLAGS_PRECISION_PARAMETER)
+           {
+#if TRIO_ERRORS
+             usedEntries[precision] += 1;
+#endif
+             parameters[pos].type = FORMAT_PARAMETER;
+             indices[precision] = pos;
+             precision = pos++;
+           }
+         if (flags & FLAGS_BASE_PARAMETER)
+           {
+#if TRIO_ERRORS
+             usedEntries[base] += 1;
+#endif
+             parameters[pos].type = FORMAT_PARAMETER;
+             indices[base] = pos;
+             base = pos++;
+           }
+         if (flags & FLAGS_VARSIZE_PARAMETER)
+           {
+#if TRIO_ERRORS
+             usedEntries[varsize] += 1;
+#endif
+             parameters[pos].type = FORMAT_PARAMETER;
+             indices[varsize] = pos;
+             varsize = pos++;
+           }
+         
+         indices[currentParam] = pos;
+         
+         switch (format[index++])
+           {
+#if defined(SPECIFIER_CHAR_UPPER)
+           case SPECIFIER_CHAR_UPPER:
+             flags |= FLAGS_WIDECHAR;
+             /* FALLTHROUGH */
+#endif
+           case SPECIFIER_CHAR:
+             if (flags & FLAGS_LONG)
+               flags |= FLAGS_WIDECHAR;
+             else if (flags & FLAGS_SHORT)
+               flags &= ~FLAGS_WIDECHAR;
+             parameters[pos].type = FORMAT_CHAR;
+             break;
+
+#if defined(SPECIFIER_STRING_UPPER)
+           case SPECIFIER_STRING_UPPER:
+             flags |= FLAGS_WIDECHAR;
+             /* FALLTHROUGH */
+#endif
+           case SPECIFIER_STRING:
+             if (flags & FLAGS_LONG)
+               flags |= FLAGS_WIDECHAR;
+             else if (flags & FLAGS_SHORT)
+               flags &= ~FLAGS_WIDECHAR;
+             parameters[pos].type = FORMAT_STRING;
+             break;
+
+
+           case SPECIFIER_GROUP:
+             if (TYPE_SCAN == type)
+               {
+                 int depth = 1;
+                 parameters[pos].type = FORMAT_GROUP;
+                 if (format[index] == QUALIFIER_CIRCUMFLEX)
+                   index++;
+                 if (format[index] == SPECIFIER_UNGROUP)
+                   index++;
+                 if (format[index] == QUALIFIER_MINUS)
+                   index++;
+                 /* Skip nested brackets */
+                 while (format[index] != NIL)
+                   {
+                     if (format[index] == SPECIFIER_GROUP)
+                       {
+                         depth++;
+                       }
+                     else if (format[index] == SPECIFIER_UNGROUP)
+                       {
+                         if (--depth <= 0)
+                           {
+                             index++;
+                             break;
+                           }
+                       }
+                     index++;
+                   }
+               }
+             break;
+             
+           case SPECIFIER_INTEGER:
+             parameters[pos].type = FORMAT_INT;
+             break;
+             
+           case SPECIFIER_UNSIGNED:
+             flags |= FLAGS_UNSIGNED;
+             parameters[pos].type = FORMAT_INT;
+             break;
+
+           case SPECIFIER_DECIMAL:
+             /* Disable base modifier */
+             flags &= ~FLAGS_BASE_PARAMETER;
+             base = BASE_DECIMAL;
+             parameters[pos].type = FORMAT_INT;
+             break;
+
+           case SPECIFIER_OCTAL:
+             flags &= ~FLAGS_BASE_PARAMETER;
+             base = BASE_OCTAL;
+             parameters[pos].type = FORMAT_INT;
+             break;
+
+#if defined(SPECIFIER_BINARY)
+           case SPECIFIER_BINARY_UPPER:
+             flags |= FLAGS_UPPER;
+             /* FALLTHROUGH */
+           case SPECIFIER_BINARY:
+             flags |= FLAGS_NILPADDING;
+             flags &= ~FLAGS_BASE_PARAMETER;
+             base = BASE_BINARY;
+             parameters[pos].type = FORMAT_INT;
+             break;
+#endif
+
+           case SPECIFIER_HEX_UPPER:
+             flags |= FLAGS_UPPER;
+             /* FALLTHROUGH */
+           case SPECIFIER_HEX:
+             flags |= FLAGS_UNSIGNED;
+             flags &= ~FLAGS_BASE_PARAMETER;
+             base = BASE_HEX;
+             parameters[pos].type = FORMAT_INT;
+             break;
+
+           case SPECIFIER_FLOAT_E_UPPER:
+             flags |= FLAGS_UPPER;
+             /* FALLTHROUGH */
+           case SPECIFIER_FLOAT_E:
+             flags |= FLAGS_FLOAT_E;
+             parameters[pos].type = FORMAT_DOUBLE;
+             break;
+
+           case SPECIFIER_FLOAT_G_UPPER:
+             flags |= FLAGS_UPPER;
+             /* FALLTHROUGH */
+           case SPECIFIER_FLOAT_G:
+             flags |= FLAGS_FLOAT_G;
+             parameters[pos].type = FORMAT_DOUBLE;
+             break;
+
+           case SPECIFIER_FLOAT_F_UPPER:
+             flags |= FLAGS_UPPER;
+             /* FALLTHROUGH */
+           case SPECIFIER_FLOAT_F:
+             parameters[pos].type = FORMAT_DOUBLE;
+             break;
+
+           case SPECIFIER_POINTER:
+             parameters[pos].type = FORMAT_POINTER;
+             break;
+
+           case SPECIFIER_COUNT:
+             parameters[pos].type = FORMAT_COUNT;
+             break;
+
+#if defined(SPECIFIER_HEXFLOAT)
+# if defined(SPECIFIER_HEXFLOAT_UPPER)
+           case SPECIFIER_HEXFLOAT_UPPER:
+             flags |= FLAGS_UPPER;
+             /* FALLTHROUGH */
+# endif
+           case SPECIFIER_HEXFLOAT:
+             base = BASE_HEX;
+             parameters[pos].type = FORMAT_DOUBLE;
+             break;
+#endif
+
+#if defined(FORMAT_ERRNO)
+           case SPECIFIER_ERRNO:
+             parameters[pos].type = FORMAT_ERRNO;
+             break;
+#endif
+
+#if defined(SPECIFIER_USER_DEFINED_BEGIN)
+           case SPECIFIER_USER_DEFINED_BEGIN:
+             {
+               unsigned int max;
+               int without_namespace = TRUE;
+               
+               parameters[pos].type = FORMAT_USER_DEFINED;
+               parameters[pos].user_name[0] = NIL;
+               tmpformat = (char *)&format[index];
+             
+               while ((ch = format[index]))
+                 {
+                   index++;
+                   if (ch == SPECIFIER_USER_DEFINED_END)
+                     {
+                       if (without_namespace)
+                         {
+                           /* We must get the handle first */
+                           parameters[pos].type = FORMAT_PARAMETER;
+                           parameters[pos].indexAfterSpecifier = index;
+                           parameters[pos].flags = FLAGS_USER_DEFINED;
+                           /* Adjust parameters for insertion of new one */
+                           pos++;
+# if TRIO_ERRORS
+                           usedEntries[currentParam] += 1;
+# endif
+                           parameters[pos].type = FORMAT_USER_DEFINED;
+                           currentParam++;
+                           indices[currentParam] = pos;
+                           if (currentParam > maxParam)
+                             maxParam = currentParam;
+                         }
+                       /* Copy the user data */
+                       max = (unsigned int)(&format[index] - tmpformat);
+                       if (max > MAX_USER_DATA)
+                         max = MAX_USER_DATA;
+                       StrCopyMax(parameters[pos].user_data,
+                                  max,
+                                  tmpformat);
+                       break; /* while */
+                     }
+                   if (ch == SPECIFIER_USER_DEFINED_SEPARATOR)
+                     {
+                       without_namespace = FALSE;
+                       /* Copy the namespace for later looking-up */
+                       max = (int)(&format[index] - tmpformat);
+                       if (max > MAX_USER_NAME)
+                         max = MAX_USER_NAME;
+                       StrCopyMax(parameters[pos].user_name,
+                                  max,
+                                  tmpformat);
+                       tmpformat = (char *)&format[index];
+                     }
+                 }
+               if (ch != SPECIFIER_USER_DEFINED_END)
+                 return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+             }
+             break;
+#endif /* defined(SPECIFIER_USER_DEFINED_BEGIN) */
+             
+           default:
+             /* Bail out completely to make the error more obvious */
+              return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+           }
+
+#if TRIO_ERRORS
+         /*  Count the number of times this entry has been used */
+         usedEntries[currentParam] += 1;
+#endif
+         
+         /* Find last sticky parameters */
+         if (got_sticky && !(flags & FLAGS_STICKY))
+           {
+             for (i = pos - 1; i >= 0; i--)
+               {
+                 if (parameters[i].type == FORMAT_PARAMETER)
+                   continue;
+                 if ((parameters[i].flags & FLAGS_STICKY) &&
+                     (parameters[i].type == parameters[pos].type))
+                   {
+                     /* Do not overwrite current qualifiers */
+                     flags |= (parameters[i].flags & (unsigned long)~FLAGS_STICKY);
+                     if (width == NO_WIDTH)
+                       width = parameters[i].width;
+                     if (precision == NO_PRECISION)
+                       precision = parameters[i].precision;
+                     if (base == NO_BASE)
+                       base = parameters[i].base;
+                     break;
+                   }
+               }
+           }
+         
+         parameters[pos].indexAfterSpecifier = index;
+         parameters[pos].flags = flags;
+         parameters[pos].width = width;
+         parameters[pos].precision = precision;
+         parameters[pos].base = (base == NO_BASE) ? BASE_DECIMAL : base;
+         parameters[pos].varsize = varsize;
+#ifdef QUALIFIER_ESCAPE
+         parameters[pos].escape = escape;
+#endif
+         pos++;
+         
+         if (! positional)
+           parameterPosition++;
+         
+       } /* if identifier */
+      
+    } /* while format characters left */
+
+  for (num = 0; num <= maxParam; num++)
+    {
+#if TRIO_ERRORS
+      if (usedEntries[num] != 1)
+       {
+         if (usedEntries[num] == 0) /* gap detected */
+           return TRIO_ERROR_RETURN(TRIO_EGAP, num);
+         else /* double references detected */
+           return TRIO_ERROR_RETURN(TRIO_EDBLREF, num);
+       }
+#endif
+      
+      i = indices[num];
+
+      /*
+       * FORMAT_PARAMETERS are only present if they must be read,
+       * so it makes no sense to check the ignore flag (besides,
+       * the flags variable is not set for that particular type)
+       */
+      if ((parameters[i].type != FORMAT_PARAMETER) &&
+         (parameters[i].flags & FLAGS_IGNORE))
+       continue; /* for all arguments */
+
+      /*
+       * The stack arguments are read according to ANSI C89
+       * default argument promotions:
+       *
+       *  char           = int
+       *  short          = int
+       *  unsigned char  = unsigned int
+       *  unsigned short = unsigned int
+       *  float          = double
+       *
+       * In addition to the ANSI C89 these types are read (the
+       * default argument promotions of C99 has not been
+       * considered yet)
+       *
+       *  long long
+       *  long double
+       *  size_t
+       *  ptrdiff_t
+       *  intmax_t
+       */
+      switch (parameters[i].type)
+       {
+       case FORMAT_GROUP:
+       case FORMAT_STRING:
+#if TRIO_WIDECHAR
+         if (flags & FLAGS_WIDECHAR)
+           {
+             parameters[i].data.wstring = (argarray == NULL)
+               ? va_arg(arglist, wchar_t *)
+               : (wchar_t *)(argarray[num]);
+           }
+         else
+#endif
+           {
+             parameters[i].data.string = (argarray == NULL)
+               ? va_arg(arglist, char *)
+               : (char *)(argarray[num]);
+           }
+         break;
+
+       case FORMAT_POINTER:
+       case FORMAT_COUNT:
+       case FORMAT_USER_DEFINED:
+       case FORMAT_UNKNOWN:
+         parameters[i].data.pointer = (argarray == NULL)
+           ? va_arg(arglist, void *)
+           : argarray[num];
+         break;
+
+       case FORMAT_CHAR:
+       case FORMAT_INT:
+         if (TYPE_SCAN == type)
+           {
+              if (argarray == NULL)
+                parameters[i].data.pointer = 
+                  (trio_uintmax_t *)va_arg(arglist, void *);
+              else
+                {
+                  if (parameters[i].type == FORMAT_CHAR)
+                    parameters[i].data.pointer =
+                      (trio_uintmax_t *)((char *)argarray[num]);
+                  else if (parameters[i].flags & FLAGS_SHORT)
+                    parameters[i].data.pointer =
+                      (trio_uintmax_t *)((short *)argarray[num]);
+                  else
+                    parameters[i].data.pointer =
+                      (trio_uintmax_t *)((int *)argarray[num]);
+                }
+           }
+         else
+           {
+#if defined(QUALIFIER_VARSIZE) || defined(QUALIFIER_FIXED_SIZE)
+             if ((parameters[i].flags & FLAGS_VARSIZE_PARAMETER) ||
+                 (parameters[i].flags & FLAGS_FIXED_SIZE))
+               {
+                 if (parameters[i].flags & FLAGS_VARSIZE_PARAMETER)
+                   {
+                     /*
+                      * Variable sizes are mapped onto the fixed sizes, in
+                      * accordance with integer promotion.
+                      *
+                      * Please note that this may not be portable, as we
+                      * only guess the size, not the layout of the numbers.
+                      * For example, if int is little-endian, and long is
+                      * big-endian, then this will fail.
+                      */
+                     varsize = (int)parameters[parameters[i].varsize].data.number.as_unsigned;
+                   }
+                 else
+                   {
+                     /* Used for the I<bits> modifiers */
+                     varsize = parameters[i].varsize;
+                   }
+                 parameters[i].flags &= ~FLAGS_ALL_VARSIZES;
+                 
+                 if (varsize <= (int)sizeof(int))
+                   ;
+                 else if (varsize <= (int)sizeof(long))
+                   parameters[i].flags |= FLAGS_LONG;
+#if defined(QUALIFIER_INTMAX_T)
+                 else if (varsize <= (int)sizeof(trio_longlong_t))
+                   parameters[i].flags |= FLAGS_QUAD;
+                 else
+                   parameters[i].flags |= FLAGS_INTMAX_T;
+#else
+                 else
+                   parameters[i].flags |= FLAGS_QUAD;
+#endif
+               }
+#endif /* defined(QUALIFIER_VARSIZE) */
+#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
+             if (parameters[i].flags & FLAGS_SIZE_T)
+               parameters[i].data.number.as_unsigned = (argarray == NULL)
+                 ? (trio_uintmax_t)va_arg(arglist, size_t)
+                 : (trio_uintmax_t)(*((size_t *)argarray[num]));
+             else
+#endif
+#if defined(QUALIFIER_PTRDIFF_T)
+             if (parameters[i].flags & FLAGS_PTRDIFF_T)
+               parameters[i].data.number.as_unsigned = (argarray == NULL)
+                 ? (trio_uintmax_t)va_arg(arglist, ptrdiff_t)
+                 : (trio_uintmax_t)(*((ptrdiff_t *)argarray[num]));
+             else
+#endif
+#if defined(QUALIFIER_INTMAX_T)
+             if (parameters[i].flags & FLAGS_INTMAX_T)
+               parameters[i].data.number.as_unsigned = (argarray == NULL)
+                 ? (trio_uintmax_t)va_arg(arglist, trio_intmax_t)
+                 : (trio_uintmax_t)(*((trio_intmax_t *)argarray[num]));
+             else
+#endif
+             if (parameters[i].flags & FLAGS_QUAD)
+               parameters[i].data.number.as_unsigned = (argarray == NULL)
+                 ? (trio_uintmax_t)va_arg(arglist, trio_ulonglong_t)
+                 : (trio_uintmax_t)(*((trio_ulonglong_t *)argarray[num]));
+             else if (parameters[i].flags & FLAGS_LONG)
+               parameters[i].data.number.as_unsigned = (argarray == NULL)
+                 ? (trio_uintmax_t)va_arg(arglist, long)
+                 : (trio_uintmax_t)(*((long *)argarray[num]));
+             else
+               {
+                 if (argarray == NULL)
+                   parameters[i].data.number.as_unsigned = (trio_uintmax_t)va_arg(arglist, int);
+                 else
+                   {
+                     if (parameters[i].type == FORMAT_CHAR)
+                       parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((char *)argarray[num]));
+                     else if (parameters[i].flags & FLAGS_SHORT)
+                       parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((short *)argarray[num]));
+                     else
+                       parameters[i].data.number.as_unsigned = (trio_uintmax_t)(*((int *)argarray[num]));
+                   }
+               }
+           }
+         break;
+
+       case FORMAT_PARAMETER:
+         /*
+          * The parameter for the user-defined specifier is a pointer,
+          * whereas the rest (width, precision, base) uses an integer.
+          */
+         if (parameters[i].flags & FLAGS_USER_DEFINED)
+           parameters[i].data.pointer = (argarray == NULL)
+             ? va_arg(arglist, void *)
+             : argarray[num];
+         else
+           parameters[i].data.number.as_unsigned = (argarray == NULL)
+             ? (trio_uintmax_t)va_arg(arglist, int)
+             : (trio_uintmax_t)(*((int *)argarray[num]));
+         break;
+
+       case FORMAT_DOUBLE:
+         if (TYPE_SCAN == type)
+           {
+             if (parameters[i].flags & FLAGS_LONG)
+               parameters[i].data.longdoublePointer = (argarray == NULL)
+                 ? va_arg(arglist, long double *)
+                 : (long double *)((long double *)argarray[num]);
+             else
+                {
+                  if (argarray == NULL)
+                    parameters[i].data.doublePointer =
+                      va_arg(arglist, double *);
+                 else
+                   {
+                     if (parameters[i].flags & FLAGS_SHORT)
+                       parameters[i].data.doublePointer =
+                         (double *)((float *)argarray[num]);
+                     else
+                       parameters[i].data.doublePointer =
+                         (double *)((double *)argarray[num]);
+                   }
+                }
+           }
+         else
+           {
+             if (parameters[i].flags & FLAGS_LONG)
+               parameters[i].data.longdoubleNumber = (argarray == NULL)
+                 ? va_arg(arglist, long double)
+                 : (long double)(*((long double *)argarray[num]));
+             else
+               {
+                 if (argarray == NULL)
+                   parameters[i].data.longdoubleNumber = (long double)va_arg(arglist, double);
+                 else
+                   {
+                     if (parameters[i].flags & FLAGS_SHORT)
+                       parameters[i].data.longdoubleNumber = (long double)(*((float *)argarray[num]));
+                     else
+                       parameters[i].data.longdoubleNumber = (long double)(long double)(*((double *)argarray[num]));
+                   }
+               }
+           }
+         break;
+
+#if defined(FORMAT_ERRNO)
+       case FORMAT_ERRNO:
+         parameters[i].data.errorNumber = errno;
+         break;
+#endif
+
+       default:
+         break;
+       }
+    } /* for all specifiers */
+  return num;
+}
+
+
+/*************************************************************************
+ *
+ * @FORMATTING
+ *
+ ************************************************************************/
+
+
+/*************************************************************************
+ * TrioWriteNumber [private]
+ *
+ * Description:
+ *  Output a number.
+ *  The complexity of this function is a result of the complexity
+ *  of the dependencies of the flags.
+ */
+static void
+TrioWriteNumber(trio_T *self,
+               trio_uintmax_t number,
+               unsigned long flags,
+               int width,
+               int precision,
+               int base)
+{
+  BOOLEAN_T isNegative;
+  char buffer[MAX_CHARS_IN(trio_uintmax_t) * (1 + MAX_LOCALE_SEPARATOR_LENGTH) + 1];
+  char *bufferend;
+  char *pointer;
+  const char *digits;
+  int i;
+  int length;
+  char *p;
+  int charsPerThousand;
+  int groupingIndex;
+  int count;
+
+  assert(VALID(self));
+  assert(VALID(self->OutStream));
+  assert((base >= MIN_BASE && base <= MAX_BASE) || (base == NO_BASE));
+
+  digits = (flags & FLAGS_UPPER) ? internalDigitsUpper : internalDigitsLower;
+
+  isNegative = (flags & FLAGS_UNSIGNED)
+    ? FALSE
+    : ((trio_intmax_t)number < 0);
+  if (isNegative)
+    number = -number;
+
+  if (flags & FLAGS_QUAD)
+    number &= (trio_ulonglong_t)-1;
+  else if (flags & FLAGS_LONG)
+    number &= (unsigned long)-1;
+  else
+    number &= (unsigned int)-1;
+  
+  /* Build number */
+  pointer = bufferend = &buffer[sizeof(buffer) - 1];
+  *pointer-- = NIL;
+  charsPerThousand = (int)internalGrouping[0];
+  groupingIndex = 1;
+  for (i = 1; i < (int)sizeof(buffer); i++)
+    {
+      *pointer-- = digits[number % base];
+      number /= base;
+      if (number == 0)
+       break;
+
+      if ((flags & FLAGS_QUOTE)
+         && (charsPerThousand != NO_GROUPING)
+         && (i % charsPerThousand == 0))
+       {
+         /*
+          * We are building the number from the least significant
+          * to the most significant digit, so we have to copy the
+          * thousand separator backwards
+          */
+         length = StrLength(internalThousandSeparator);
+         if (((int)(pointer - buffer) - length) > 0)
+           {
+             p = &internalThousandSeparator[length - 1];
+             while (length-- > 0)
+               *pointer-- = *p--;
+           }
+
+         /* Advance to next grouping number */
+         switch (internalGrouping[groupingIndex])
+           {
+           case CHAR_MAX: /* Disable grouping */
+             charsPerThousand = NO_GROUPING;
+             break;
+           case 0: /* Repeat last group */
+             break;
+           default:
+             charsPerThousand = (int)internalGrouping[groupingIndex++];
+             break;
+           }
+       }
+    }
+
+  /* Adjust width */
+  width -= (bufferend - pointer) - 1;
+
+  /* Adjust precision */
+  if (NO_PRECISION != precision)
+    {
+      precision -= (bufferend - pointer) - 1;
+      if (precision < 0)
+       precision = 0;
+      flags |= FLAGS_NILPADDING;
+    }
+
+  /* Adjust width further */
+  if (isNegative || (flags & FLAGS_SHOWSIGN) || (flags & FLAGS_SPACE))
+    width--;
+  if (flags & FLAGS_ALTERNATIVE)
+    {
+      switch (base)
+       {
+       case BASE_BINARY:
+       case BASE_HEX:
+         width -= 2;
+         break;
+       case BASE_OCTAL:
+         width--;
+         break;
+       default:
+         break;
+       }
+    }
+
+  /* Output prefixes spaces if needed */
+  if (! ((flags & FLAGS_LEFTADJUST) ||
+        ((flags & FLAGS_NILPADDING) && (precision == NO_PRECISION))))
+    {
+      count = (precision == NO_PRECISION) ? 0 : precision;
+      while (width-- > count)
+       self->OutStream(self, CHAR_ADJUST);
+    }
+
+  /* width has been adjusted for signs and alternatives */
+  if (isNegative)
+    self->OutStream(self, '-');
+  else if (flags & FLAGS_SHOWSIGN)
+    self->OutStream(self, '+');
+  else if (flags & FLAGS_SPACE)
+    self->OutStream(self, ' ');
+
+  if (flags & FLAGS_ALTERNATIVE)
+    {
+      switch (base)
+       {
+       case BASE_BINARY:
+         self->OutStream(self, '0');
+         self->OutStream(self, (flags & FLAGS_UPPER) ? 'B' : 'b');
+         break;
+
+       case BASE_OCTAL:
+         self->OutStream(self, '0');
+         break;
+
+       case BASE_HEX:
+         self->OutStream(self, '0');
+         self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x');
+         break;
+
+       default:
+         break;
+       } /* switch base */
+    }
+
+  /* Output prefixed zero padding if needed */
+  if (flags & FLAGS_NILPADDING)
+    {
+      if (precision == NO_PRECISION)
+       precision = width;
+      while (precision-- > 0)
+       {
+         self->OutStream(self, '0');
+         width--;
+       }
+    }
+
+  /* Output the number itself */
+  while (*(++pointer))
+    {
+      self->OutStream(self, *pointer);
+    }
+
+  /* Output trailing spaces if needed */
+  if (flags & FLAGS_LEFTADJUST)
+    {
+      while (width-- > 0)
+       self->OutStream(self, CHAR_ADJUST);
+    }
+}
+
+/*************************************************************************
+ * TrioWriteStringCharacter [private]
+ *
+ * Description:
+ *  Output a single character of a string
+ */
+static void
+TrioWriteStringCharacter(trio_T *self,
+                        int ch,
+                        unsigned long flags)
+{
+  if (flags & FLAGS_ALTERNATIVE)
+    {
+      if (! (isprint(ch) || isspace(ch)))
+       {
+         /*
+          * Non-printable characters are converted to C escapes or
+          * \number, if no C escape exists.
+          */
+         self->OutStream(self, CHAR_BACKSLASH);
+         switch (ch)
+           {
+           case '\007': self->OutStream(self, 'a'); break;
+           case '\b': self->OutStream(self, 'b'); break;
+           case '\f': self->OutStream(self, 'f'); break;
+           case '\n': self->OutStream(self, 'n'); break;
+           case '\r': self->OutStream(self, 'r'); break;
+           case '\t': self->OutStream(self, 't'); break;
+           case '\v': self->OutStream(self, 'v'); break;
+           case '\\': self->OutStream(self, '\\'); break;
+           default:
+             self->OutStream(self, 'x');
+             TrioWriteNumber(self, (trio_intmax_t)ch,
+                             FLAGS_UNSIGNED | FLAGS_NILPADDING,
+                             2, 2, BASE_HEX);
+             break;
+           }
+       }
+      else if (ch == CHAR_BACKSLASH)
+       {
+         self->OutStream(self, CHAR_BACKSLASH);
+         self->OutStream(self, CHAR_BACKSLASH);
+       }
+      else
+       {
+         self->OutStream(self, ch);
+       }
+    }
+  else
+    {
+      self->OutStream(self, ch);
+    }
+}
+
+/*************************************************************************
+ * TrioWriteString [private]
+ *
+ * Description:
+ *  Output a string
+ */
+static void
+TrioWriteString(trio_T *self,
+               const char *string,
+               unsigned long flags,
+               int width,
+               int precision)
+{
+  int length;
+  int ch;
+
+  assert(VALID(self));
+  assert(VALID(self->OutStream));
+
+  if (string == NULL)
+    {
+      string = null;
+      length = sizeof(null) - 1;
+      /* Disable quoting for the null pointer */
+      flags &= (~FLAGS_QUOTE);
+      width = 0;
+    }
+  else
+    {
+      length = StrLength(string);
+    }
+  if ((NO_PRECISION != precision) &&
+      (precision < length))
+    {
+      length = precision;
+    }
+  width -= length;
+
+  if (flags & FLAGS_QUOTE)
+    self->OutStream(self, CHAR_QUOTE);
+
+  if (! (flags & FLAGS_LEFTADJUST))
+    {
+      while (width-- > 0)
+       self->OutStream(self, CHAR_ADJUST);
+    }
+
+  while (length-- > 0)
+    {
+      /* The ctype parameters must be an unsigned char (or EOF) */
+      ch = (int)((unsigned char)(*string++));
+      TrioWriteStringCharacter(self, ch, flags);
+    }
+
+  if (flags & FLAGS_LEFTADJUST)
+    {
+      while (width-- > 0)
+       self->OutStream(self, CHAR_ADJUST);
+    }
+  if (flags & FLAGS_QUOTE)
+    self->OutStream(self, CHAR_QUOTE);
+}
+
+/*************************************************************************
+ * TrioWriteWideStringCharacter [private]
+ *
+ * Description:
+ *  Output a wide string as a multi-byte sequence
+ */
+#if TRIO_WIDECHAR
+static int
+TrioWriteWideStringCharacter(trio_T *self,
+                            wchar_t wch,
+                            unsigned long flags,
+                            int width)
+{
+  int size;
+  int i;
+  int ch;
+  char *string;
+  char buffer[MB_LEN_MAX + 1];
+
+  if (width == NO_WIDTH)
+    width = sizeof(buffer);
+  
+  size = wctomb(buffer, wch);
+  if ((size <= 0) || (size > width) || (buffer[0] == NIL))
+    return 0;
+
+  string = buffer;
+  i = size;
+  while ((width >= i) && (width-- > 0) && (i-- > 0))
+    {
+      /* The ctype parameters must be an unsigned char (or EOF) */
+      ch = (int)((unsigned char)(*string++));
+      TrioWriteStringCharacter(self, ch, flags);
+    }
+  return size;
+}
+#endif /* TRIO_WIDECHAR */
+
+/*************************************************************************
+ * TrioWriteString [private]
+ *
+ * Description:
+ *  Output a wide character string as a multi-byte string
+ */
+#if TRIO_WIDECHAR
+static void
+TrioWriteWideString(trio_T *self,
+                   const wchar_t *wstring,
+                   unsigned long flags,
+                   int width,
+                   int precision)
+{
+  int length;
+  int size;
+
+  assert(VALID(self));
+  assert(VALID(self->OutStream));
+
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  mblen(NULL, 0);
+#endif
+  
+  if (wstring == NULL)
+    {
+      TrioWriteString(self, NULL, flags, width, precision);
+      return;
+    }
+  
+  if (NO_PRECISION == precision)
+    {
+      length = INT_MAX;
+    }
+  else
+    {
+      length = precision;
+      width -= length;
+    }
+
+  if (flags & FLAGS_QUOTE)
+    self->OutStream(self, CHAR_QUOTE);
+
+  if (! (flags & FLAGS_LEFTADJUST))
+    {
+      while (width-- > 0)
+       self->OutStream(self, CHAR_ADJUST);
+    }
+
+  while (length > 0)
+    {
+      size = TrioWriteWideStringCharacter(self, *wstring++, flags, length);
+      if (size == 0)
+       break; /* while */
+      length -= size;
+    }
+
+  if (flags & FLAGS_LEFTADJUST)
+    {
+      while (width-- > 0)
+       self->OutStream(self, CHAR_ADJUST);
+    }
+  if (flags & FLAGS_QUOTE)
+    self->OutStream(self, CHAR_QUOTE);
+}
+#endif /* TRIO_WIDECHAR */
+
+/*************************************************************************
+ * TrioWriteDouble [private]
+ */
+static void
+TrioWriteDouble(trio_T *self,
+               long double longdoubleNumber,
+               unsigned long flags,
+               int width,
+               int precision,
+               int base)
+{
+  int charsPerThousand;
+  int length;
+  double number;
+  double workNumber;
+  int integerDigits;
+  int fractionDigits;
+  int exponentDigits;
+  int expectedWidth;
+  int exponent;
+  unsigned int uExponent = 0;
+  double dblBase;
+  BOOLEAN_T isNegative;
+  BOOLEAN_T isExponentNegative = FALSE;
+  BOOLEAN_T isHex;
+  const char *digits;
+  char numberBuffer[MAX_MANTISSA_DIGITS
+                  * (1 + MAX_LOCALE_SEPARATOR_LENGTH) + 1];
+  char *numberPointer;
+  char exponentBuffer[MAX_EXPONENT_DIGITS + 1];
+  char *exponentPointer = NULL;
+  int groupingIndex;
+  char *work;
+  int i;
+  BOOLEAN_T onlyzero;
+  int zeroes = 0;
+  
+  assert(VALID(self));
+  assert(VALID(self->OutStream));
+  assert(base == BASE_DECIMAL || base == BASE_HEX);
+
+  number = (double)longdoubleNumber;
+  
+  /* Look for infinite numbers and non-a-number first */
+  switch (TrioIsInfinite(number))
+    {
+    case 1:
+      /* Positive infinity */
+      TrioWriteString(self,
+                     (flags & FLAGS_UPPER)
+                     ? INFINITE_UPPER
+                     : INFINITE_LOWER,
+                     flags, width, precision);
+      return;
+
+    case -1:
+      /* Negative infinity */
+      TrioWriteString(self,
+                     (flags & FLAGS_UPPER)
+                     ? "-" INFINITE_UPPER
+                     : "-" INFINITE_LOWER,
+                     flags, width, precision);
+      return;
+
+    default:
+      /* Finitude */
+      break;
+    }
+  if (TrioIsNan(number))
+    {
+      TrioWriteString(self,
+                     (flags & FLAGS_UPPER)
+                     ? NAN_UPPER
+                     : NAN_LOWER,
+                     flags, width, precision);
+      return;
+    }
+
+  /* Normal numbers */
+  digits = (flags & FLAGS_UPPER) ? internalDigitsUpper : internalDigitsLower;
+  isHex = (base == BASE_HEX);
+  dblBase = (double)base;
+  
+  if (precision == NO_PRECISION)
+    precision = FLT_DIG;
+  
+  isNegative = (number < 0.0);
+  if (isNegative)
+    number = -number;
+
+  if ((flags & FLAGS_FLOAT_G) || isHex)
+    {
+      if (precision == 0)
+       precision = 1;
+
+      if ((number < 1.0e-4) || (number > pow(10.0, (double)precision)))
+       {
+         /* Use scientific notation */
+         flags |= FLAGS_FLOAT_E;
+       }
+      else if (number < 1.0)
+       {
+         /*
+          * Use normal notation. If the integer part of the number is
+          * zero, then adjust the precision to include leading fractional
+          * zeros.
+          */
+         workNumber = fabs(guarded_log10(number));
+         if (workNumber - floor(workNumber) < 0.001)
+           workNumber--;
+         zeroes = (int)floor(workNumber);
+       }
+    }
+
+  if (flags & FLAGS_FLOAT_E)
+    {
+      /* Scale the number */
+      workNumber = guarded_log10(number);
+      if (workNumber == -HUGE_VAL)
+       {
+         exponent = 0;
+         /* Undo setting */
+         if (flags & FLAGS_FLOAT_G)
+           flags &= ~FLAGS_FLOAT_E;
+       }
+      else
+       {
+         exponent = (int)floor(workNumber);
+         number /= pow(10.0, (double)exponent);
+         isExponentNegative = (exponent < 0);
+         uExponent = (isExponentNegative) ? -exponent : exponent;
+         /* No thousand separators */
+         flags &= ~FLAGS_QUOTE;
+       }
+    }
+
+  /*
+   * Truncated number.
+   *
+   * precision is number of significant digits for FLOAT_G
+   * and number of fractional digits for others
+   */
+  integerDigits = (floor(number) > DBL_EPSILON)
+    ? 1 + (int)guarded_log10(floor(number))
+    : 1;
+  fractionDigits = ((flags & FLAGS_FLOAT_G) && (zeroes == 0))
+    ? precision - integerDigits
+    : zeroes + precision;
+  
+  number = floor(0.5 + number * pow(dblBase, (double)fractionDigits));
+  workNumber = (isHex
+               ? guarded_log16(0.5 + number)
+               : guarded_log10(0.5 + number));
+  if ((int)workNumber + 1 > integerDigits + fractionDigits)
+    {
+      if (flags & FLAGS_FLOAT_E)
+       {
+         /* Adjust if number was rounded up one digit (ie. 0.99 to 1.00) */
+         exponent--;
+         uExponent -= (isExponentNegative) ? 1 : -1;
+         number /= dblBase;
+       }
+      else
+       {
+         /* Adjust if number was rounded up one digit (ie. 99 to 100) */
+         integerDigits++;
+       }
+    }
+  
+  /* Build the fraction part */
+  numberPointer = &numberBuffer[sizeof(numberBuffer) - 1];
+  *numberPointer = NIL;
+  onlyzero = TRUE;
+  for (i = 0; i < fractionDigits; i++)
+    {
+      *(--numberPointer) = digits[(int)fmod(number, dblBase)];
+      number = floor(number / dblBase);
+
+      if ((flags & FLAGS_FLOAT_G) && !(flags & FLAGS_ALTERNATIVE))
+        {
+          /* Prune trailing zeroes */
+          if (numberPointer[0] != digits[0])
+            onlyzero = FALSE;
+          else if (onlyzero && (numberPointer[0] == digits[0]))
+            numberPointer++;
+        }
+      else
+        onlyzero = FALSE;
+    }
+  
+  /* Insert decimal point */
+  if ((flags & FLAGS_ALTERNATIVE) || ((fractionDigits > 0) && !onlyzero))
+    {
+      i = StrLength(internalDecimalPoint);
+      while (i> 0)
+       {
+         *(--numberPointer) = internalDecimalPoint[--i];
+       }
+    }
+  /* Insert the integer part and thousand separators */
+  charsPerThousand = (int)internalGrouping[0];
+  groupingIndex = 1;
+  for (i = 1; i < integerDigits + 1; i++)
+    {
+      *(--numberPointer) = digits[(int)fmod(number, dblBase)];
+      number = floor(number / dblBase);
+      if (number < DBL_EPSILON)
+       break;
+
+      if ((i > 0)
+         && ((flags & (FLAGS_FLOAT_E | FLAGS_QUOTE)) == FLAGS_QUOTE)
+         && (charsPerThousand != NO_GROUPING)
+         && (i % charsPerThousand == 0))
+       {
+         /*
+          * We are building the number from the least significant
+          * to the most significant digit, so we have to copy the
+          * thousand separator backwards
+          */
+         length = StrLength(internalThousandSeparator);
+         integerDigits += length;
+         if (((int)(numberPointer - numberBuffer) - length) > 0)
+           {
+             work = &internalThousandSeparator[length - 1];
+             while (length-- > 0)
+               *(--numberPointer) = *work--;
+           }
+
+         /* Advance to next grouping number */
+         if (charsPerThousand != NO_GROUPING)
+           {
+             switch (internalGrouping[groupingIndex])
+               {
+               case CHAR_MAX: /* Disable grouping */
+                 charsPerThousand = NO_GROUPING;
+                 break;
+               case 0: /* Repeat last group */
+                 break;
+               default:
+                 charsPerThousand = (int)internalGrouping[groupingIndex++];
+                 break;
+               }
+           }
+       }
+    }
+  
+  /* Build the exponent */
+  exponentDigits = 0;
+  if (flags & FLAGS_FLOAT_E)
+    {
+      exponentPointer = &exponentBuffer[sizeof(exponentBuffer) - 1];
+      *exponentPointer-- = NIL;
+      do {
+       *exponentPointer-- = digits[uExponent % base];
+       uExponent /= base;
+       exponentDigits++;
+      } while (uExponent);
+    }
+
+  /*
+   * Calculate expected width.
+   *  sign + integer part + thousands separators + decimal point
+   *  + fraction + exponent
+   */
+  expectedWidth = StrLength(numberPointer);
+  if (isNegative || (flags & FLAGS_SHOWSIGN))
+    expectedWidth += sizeof("-") - 1;
+  if (exponentDigits > 0)
+    expectedWidth += exponentDigits +
+      ((exponentDigits > 1) ? sizeof("E+") : sizeof("E+0")) - 1;
+  if (isHex)
+    expectedWidth += sizeof("0X") - 1;
+  
+  /* Output prefixing */
+  if (flags & FLAGS_NILPADDING)
+    {
+      /* Leading zeros must be after sign */
+      if (isNegative)
+       self->OutStream(self, '-');
+      else if (flags & FLAGS_SHOWSIGN)
+       self->OutStream(self, '+');
+      if (isHex)
+       {
+         self->OutStream(self, '0');
+         self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x');
+       }
+      if (!(flags & FLAGS_LEFTADJUST))
+       {
+         for (i = expectedWidth; i < width; i++)
+           {
+             self->OutStream(self, '0');
+           }
+       }
+    }
+  else
+    {
+      /* Leading spaces must be before sign */
+      if (!(flags & FLAGS_LEFTADJUST))
+       {
+         for (i = expectedWidth; i < width; i++)
+           {
+             self->OutStream(self, CHAR_ADJUST);
+           }
+       }
+      if (isNegative)
+       self->OutStream(self, '-');
+      else if (flags & FLAGS_SHOWSIGN)
+       self->OutStream(self, '+');
+      if (isHex)
+       {
+         self->OutStream(self, '0');
+         self->OutStream(self, (flags & FLAGS_UPPER) ? 'X' : 'x');
+       }
+    }
+  /* Output number */
+  for (i = 0; numberPointer[i]; i++)
+    {
+      self->OutStream(self, numberPointer[i]);
+    }
+  /* Output exponent */
+  if (exponentDigits > 0)
+    {
+      self->OutStream(self,
+                     isHex
+                     ? ((flags & FLAGS_UPPER) ? 'P' : 'p')
+                     : ((flags & FLAGS_UPPER) ? 'E' : 'e'));
+      self->OutStream(self, (isExponentNegative) ? '-' : '+');
+
+      /* The exponent must contain at least two digits */
+      if (exponentDigits == 1)
+        self->OutStream(self, '0');
+
+      for (i = 0; i < exponentDigits; i++)
+       {
+         self->OutStream(self, exponentPointer[i + 1]);
+       }
+    }
+  /* Output trailing spaces */
+  if (flags & FLAGS_LEFTADJUST)
+    {
+      for (i = expectedWidth; i < width; i++)
+       {
+         self->OutStream(self, CHAR_ADJUST);
+       }
+    }
+}
+
+/*************************************************************************
+ * TrioFormatProcess [private]
+ */
+static int
+TrioFormatProcess(trio_T *data,
+                 const char *format,
+                 parameter_T *parameters)
+
+{
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  int charlen;
+#endif
+  int i;
+  const char *string;
+  void *pointer;
+  unsigned long flags;
+  int width;
+  int precision;
+  int base;
+  int index;
+  
+  index = 0;
+  i = 0;
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  mblen(NULL, 0);
+#endif
+  
+  while (format[index])
+    {
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+      if (! isascii(format[index]))
+       {
+         charlen = mblen(&format[index], MB_LEN_MAX);
+         while (charlen-- > 0)
+           {
+             data->OutStream(data, format[index++]);
+           }
+         continue; /* while */
+       }
+#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */
+      if (CHAR_IDENTIFIER == format[index])
+       {
+         if (CHAR_IDENTIFIER == format[index + 1])
+           {
+             data->OutStream(data, CHAR_IDENTIFIER);
+             index += 2;
+           }
+         else
+           {
+             /* Skip the parameter entries */
+             while (parameters[i].type == FORMAT_PARAMETER)
+               i++;
+             
+             flags = parameters[i].flags;
+
+             /* Find width */
+             width = parameters[i].width;
+             if (flags & FLAGS_WIDTH_PARAMETER)
+               {
+                 /* Get width from parameter list */
+                 width = (int)parameters[width].data.number.as_signed;
+               }
+             
+             /* Find precision */
+             if (flags & FLAGS_PRECISION)
+               {
+                 precision = parameters[i].precision;
+                 if (flags & FLAGS_PRECISION_PARAMETER)
+                   {
+                     /* Get precision from parameter list */
+                     precision = (int)parameters[precision].data.number.as_signed;
+                   }
+               }
+             else
+               {
+                 precision = NO_PRECISION;
+               }
+
+             /* Find base */
+             base = parameters[i].base;
+             if (flags & FLAGS_BASE_PARAMETER)
+               {
+                 /* Get base from parameter list */
+                 base = (int)parameters[base].data.number.as_signed;
+               }
+             
+             switch (parameters[i].type)
+               {
+               case FORMAT_CHAR:
+                 if (flags & FLAGS_QUOTE)
+                   data->OutStream(data, CHAR_QUOTE);
+                 if (! (flags & FLAGS_LEFTADJUST))
+                   {
+                     while (--width > 0)
+                       data->OutStream(data, CHAR_ADJUST);
+                   }
+#if TRIO_WIDECHAR
+                 if (flags & FLAGS_WIDECHAR)
+                   {
+                     TrioWriteWideStringCharacter(data,
+                                                  (wchar_t)parameters[i].data.number.as_signed,
+                                                  flags,
+                                                  NO_WIDTH);
+                   }
+                 else
+#endif
+                   TrioWriteStringCharacter(data,
+                                            (int)parameters[i].data.number.as_signed,
+                                            flags);
+
+                 if (flags & FLAGS_LEFTADJUST)
+                   {
+                     while(--width > 0)
+                       data->OutStream(data, CHAR_ADJUST);
+                   }
+                 if (flags & FLAGS_QUOTE)
+                   data->OutStream(data, CHAR_QUOTE);
+
+                 break; /* FORMAT_CHAR */
+
+               case FORMAT_INT:
+                 if (base == NO_BASE)
+                   base = BASE_DECIMAL;
+
+                 TrioWriteNumber(data,
+                                 parameters[i].data.number.as_signed,
+                                 flags,
+                                 width,
+                                 precision,
+                                 base);
+
+                 break; /* FORMAT_INT */
+
+               case FORMAT_DOUBLE:
+                 TrioWriteDouble(data,
+                                 parameters[i].data.longdoubleNumber,
+                                 flags,
+                                 width,
+                                 precision,
+                                 base);
+                 break; /* FORMAT_DOUBLE */
+
+               case FORMAT_STRING:
+#if TRIO_WIDECHAR
+                 if (flags & FLAGS_WIDECHAR)
+                   {
+                     TrioWriteWideString(data,
+                                         parameters[i].data.wstring,
+                                         flags,
+                                         width,
+                                         precision);
+                   }
+                 else
+#endif
+#ifdef QUALIFIER_ESCAPE
+                 {
+                   char *s = NULL;
+                    static const char* empty = "(null)";
+                   switch (parameters[i].escape)
+                   {
+                       case ESCAPE_ULM:
+                               s = edg_wll_LogEscape(parameters[i].data.string);
+                               break;
+                       case ESCAPE_XML:
+                               s = edg_wll_EscapeXML(parameters[i].data.string);
+                               break;
+                       case ESCAPE_SQL:
+                               s = edg_wll_EscapeSQL(parameters[i].data.string);
+                               break;
+                       case ESCAPE_NONE:
+                               s = strdup(parameters[i].data.string ? parameters[i].data.string : empty);
+                               break;
+                   }
+                   TrioWriteString(data,s,flags,width,precision);
+                   free(s);
+                 }
+#else
+                 {
+                     TrioWriteString(data,
+                                     parameters[i].data.string,
+                                     flags,
+                                     width,
+                                     precision);
+                 }
+#endif
+                 break; /* FORMAT_STRING */
+
+               case FORMAT_POINTER:
+                 {
+                   reference_T reference;
+                   
+                   reference.data = data;
+                   reference.parameter = &parameters[i];
+                   trio_print_pointer(&reference, parameters[i].data.pointer);
+                 }
+                 break; /* FORMAT_POINTER */
+
+               case FORMAT_COUNT:
+                 pointer = parameters[i].data.pointer;
+                 if (NULL != pointer)
+                   {
+                     /*
+                      * C99 paragraph 7.19.6.1.8 says "the number of
+                      * characters written to the output stream so far by
+                      * this call", which is data->committed
+                      */
+#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
+                     if (flags & FLAGS_SIZE_T)
+                       *(size_t *)pointer = (size_t)data->committed;
+                     else
+#endif
+#if defined(QUALIFIER_PTRDIFF_T)
+                     if (flags & FLAGS_PTRDIFF_T)
+                       *(ptrdiff_t *)pointer = (ptrdiff_t)data->committed;
+                     else
+#endif
+#if defined(QUALIFIER_INTMAX_T)
+                     if (flags & FLAGS_INTMAX_T)
+                       *(trio_intmax_t *)pointer = (trio_intmax_t)data->committed;
+                     else
+#endif
+                     if (flags & FLAGS_QUAD)
+                       {
+                         *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)data->committed;
+                       }
+                     else if (flags & FLAGS_LONG)
+                       {
+                         *(long int *)pointer = (long int)data->committed;
+                       }
+                     else if (flags & FLAGS_SHORT)
+                       {
+                         *(short int *)pointer = (short int)data->committed;
+                       }
+                     else
+                       {
+                         *(int *)pointer = (int)data->committed;
+                       }
+                   }
+                 break; /* FORMAT_COUNT */
+
+               case FORMAT_PARAMETER:
+                 break; /* FORMAT_PARAMETER */
+
+#if defined(FORMAT_ERRNO)
+               case FORMAT_ERRNO:
+                 string = StrError(parameters[i].data.errorNumber);
+                 if (string)
+                   {
+                     TrioWriteString(data,
+                                     string,
+                                     flags,
+                                     width,
+                                     precision);
+                   }
+                 else
+                   {
+                     data->OutStream(data, '#');
+                     TrioWriteNumber(data,
+                                     (trio_intmax_t)parameters[i].data.errorNumber,
+                                     flags,
+                                     width,
+                                     precision,
+                                     BASE_DECIMAL);
+                   }
+                 break; /* FORMAT_ERRNO */
+#endif /* defined(FORMAT_ERRNO) */
+
+#if defined(FORMAT_USER_DEFINED)
+               case FORMAT_USER_DEFINED:
+                 {
+                   reference_T reference;
+                   userdef_T *def = NULL;
+
+                   if (parameters[i].user_name[0] == NIL)
+                     {
+                       /* Use handle */
+                       if ((i > 0) ||
+                           (parameters[i - 1].type == FORMAT_PARAMETER))
+                         def = (userdef_T *)parameters[i - 1].data.pointer;
+                     }
+                   else
+                     {
+                       /* Look up namespace */
+                       def = TrioFindNamespace(parameters[i].user_name, NULL);
+                     }
+                   if (def) {
+                     reference.data = data;
+                     reference.parameter = &parameters[i];
+                     def->callback(&reference);
+                   }
+                 }
+                 break;
+#endif /* defined(FORMAT_USER_DEFINED) */
+                 
+               default:
+                 break;
+               } /* switch parameter type */
+
+             /* Prepare for next */
+             index = parameters[i].indexAfterSpecifier;
+             i++;
+           }
+       }
+      else /* not identifier */
+       {
+         data->OutStream(data, format[index++]);
+       }
+    }
+  return data->processed;
+}
+
+/*************************************************************************
+ * TrioFormatRef [private]
+ */
+static int
+TrioFormatRef(reference_T *reference,
+              const char *format,
+              va_list arglist,
+              void **argarray)
+{
+  int status;
+  parameter_T parameters[MAX_PARAMETERS];
+
+  status = TrioPreprocess(TYPE_PRINT, format, parameters, arglist, argarray);
+  if (status < 0)
+    return status;
+
+  return TrioFormatProcess(reference->data, format, parameters);
+}
+
+/*************************************************************************
+ * TrioFormat [private]
+ *
+ * Description:
+ *  This is the main engine for formatting output
+ */
+static int
+TrioFormat(void *destination,
+          size_t destinationSize,
+          void (*OutStream)(trio_T *, int),
+          const char *format,
+          va_list arglist,
+          void **argarray)
+{
+  int status;
+  trio_T data;
+  parameter_T parameters[MAX_PARAMETERS];
+
+  assert(VALID(OutStream));
+  assert(VALID(format));
+
+  memset(&data, 0, sizeof(data));
+  data.OutStream = OutStream;
+  data.location = destination;
+  data.max = destinationSize;
+
+#if defined(USE_LOCALE)
+  if (NULL == internalLocaleValues)
+    {
+      TrioSetLocale();
+    }
+#endif
+
+  status = TrioPreprocess(TYPE_PRINT, format, parameters, arglist, argarray);
+  if (status < 0)
+    return status;
+
+  return TrioFormatProcess(&data, format, parameters);
+}
+
+/*************************************************************************
+ * TrioOutStreamFile [private]
+ */
+static void
+TrioOutStreamFile(trio_T *self,
+                 int output)
+{
+  FILE *file = (FILE *)self->location;
+
+  assert(VALID(self));
+  assert(VALID(file));
+
+  self->processed++;
+  self->committed++;
+  (void)fputc(output, file);
+}
+
+/*************************************************************************
+ * TrioOutStreamFileDescriptor [private]
+ */
+static void
+TrioOutStreamFileDescriptor(trio_T *self,
+                           int output)
+{
+  int fd = *((int *)self->location);
+  char ch;
+
+  assert(VALID(self));
+
+  ch = (char)output;
+  (void)write(fd, &ch, sizeof(char));
+  self->processed++;
+  self->committed++;
+}
+
+/*************************************************************************
+ * TrioOutStreamString [private]
+ */
+static void
+TrioOutStreamString(trio_T *self,
+                   int output)
+{
+  char **buffer = (char **)self->location;
+
+  assert(VALID(self));
+  assert(VALID(buffer));
+
+  **buffer = (char)output;
+  (*buffer)++;
+  self->processed++;
+  self->committed++;
+}
+
+/*************************************************************************
+ * TrioOutStreamStringMax [private]
+ */
+static void
+TrioOutStreamStringMax(trio_T *self,
+                      int output)
+{
+  char **buffer;
+
+  assert(VALID(self));
+  buffer = (char **)self->location;
+  assert(VALID(buffer));
+
+  if (self->processed < self->max)
+    {
+      **buffer = (char)output;
+      (*buffer)++;
+      self->committed++;
+    }
+  self->processed++;
+}
+
+/*************************************************************************
+ * TrioOutStreamStringDynamic [private]
+ */
+#define DYNAMIC_START_SIZE 32
+struct dynamicBuffer {
+  char *buffer;
+  size_t length;
+  size_t allocated;
+};
+
+static void
+TrioOutStreamStringDynamic(trio_T *self,
+                          int output)
+{
+  struct dynamicBuffer *infop;
+  
+  assert(VALID(self));
+  assert(VALID(self->location));
+
+  infop = (struct dynamicBuffer *)self->location;
+
+  if (infop->buffer == NULL)
+    {
+      /* Start with a reasonable size */
+      infop->buffer = (char *)TRIO_MALLOC(DYNAMIC_START_SIZE);
+      if (infop->buffer == NULL)
+       return; /* fail */
+      
+      infop->allocated = DYNAMIC_START_SIZE;
+      self->processed = 0;
+      self->committed = 0;
+    }
+  else if (self->committed + sizeof(NIL) >= infop->allocated)
+    {
+      char *newptr;
+      
+      /* Allocate increasing chunks */
+      newptr = (char *)TRIO_REALLOC(infop->buffer, infop->allocated * 2);
+      
+      if (newptr == NULL)
+       return;
+
+      infop->buffer = newptr;
+      infop->allocated *= 2;
+    }
+  
+  infop->buffer[self->committed] = (char)output;
+  self->committed++;
+  self->processed++;
+  
+  infop->length = self->committed;
+}
+
+/*************************************************************************
+ * printf
+ */
+int
+trio_printf(const char *format,
+           ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioFormat(stdout, 0, TrioOutStreamFile, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vprintf(const char *format,
+            va_list args)
+{
+  assert(VALID(format));
+
+  return TrioFormat(stdout, 0, TrioOutStreamFile, format, args, NULL);
+}
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+static void shutup_unitialized(va_list *dummy UNUSED_VAR) {
+}
+
+int
+trio_printfv(const char *format,
+            void ** args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(format));
+
+  return TrioFormat(stdout, 0, TrioOutStreamFile, format, dummy, args);
+}
+
+/*************************************************************************
+ * fprintf
+ */
+int
+trio_fprintf(FILE *file,
+            const char *format,
+            ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(file));
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioFormat(file, 0, TrioOutStreamFile, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vfprintf(FILE *file,
+             const char *format,
+             va_list args)
+{
+  assert(VALID(file));
+  assert(VALID(format));
+  
+  return TrioFormat(file, 0, TrioOutStreamFile, format, args, NULL);
+}
+
+int
+trio_fprintfv(FILE *file,
+             const char *format,
+             void ** args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(file));
+  assert(VALID(format));
+  
+  return TrioFormat(file, 0, TrioOutStreamFile, format, dummy, args);
+}
+
+/*************************************************************************
+ * dprintf
+ */
+int
+trio_dprintf(int fd,
+            const char *format,
+            ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vdprintf(int fd,
+             const char *format,
+             va_list args)
+{
+  assert(VALID(format));
+  
+  return TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, args, NULL);
+}
+
+int
+trio_dprintfv(int fd,
+             const char *format,
+             void **args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(format));
+  
+  return TrioFormat(&fd, 0, TrioOutStreamFileDescriptor, format, dummy, args);
+}
+
+/*************************************************************************
+ * sprintf
+ */
+int
+trio_sprintf(char *buffer,
+            const char *format,
+            ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioFormat(&buffer, 0, TrioOutStreamString, format, args, NULL);
+  *buffer = NIL; /* Terminate with NIL character */
+  va_end(args);
+  return status;
+}
+
+int
+trio_vsprintf(char *buffer,
+             const char *format,
+             va_list args)
+{
+  int status;
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  status = TrioFormat(&buffer, 0, TrioOutStreamString, format, args, NULL);
+  *buffer = NIL;
+  return status;
+}
+
+int
+trio_sprintfv(char *buffer,
+             const char *format,
+             void **args)
+{
+  int status;
+  va_list dummy;
+  shutup_unitialized(&dummy);
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  status = TrioFormat(&buffer, 0, TrioOutStreamString, format, dummy, args);
+  *buffer = NIL;
+  return status;
+}
+
+/*************************************************************************
+ * snprintf
+ */
+int
+trio_snprintf(char *buffer,
+             size_t bufferSize,
+             const char *format,
+             ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  va_start(args, format);
+  status = TrioFormat(&buffer, bufferSize > 0 ? bufferSize - 1 : 0,
+                     TrioOutStreamStringMax, format, args, NULL);
+  if (bufferSize > 0)
+    *buffer = NIL;
+  va_end(args);
+  return status;
+}
+
+int
+trio_vsnprintf(char *buffer,
+              size_t bufferSize,
+              const char *format,
+              va_list args)
+{
+  int status;
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  status = TrioFormat(&buffer, bufferSize > 0 ? bufferSize - 1 : 0,
+                     TrioOutStreamStringMax, format, args, NULL);
+  if (bufferSize > 0)
+    *buffer = NIL;
+  return status;
+}
+
+int
+trio_snprintfv(char *buffer,
+              size_t bufferSize,
+              const char *format,
+              void **args)
+{
+  int status;
+  va_list dummy;
+  shutup_unitialized(&dummy);
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  status = TrioFormat(&buffer, bufferSize > 0 ? bufferSize - 1 : 0,
+                     TrioOutStreamStringMax, format, dummy, args);
+  if (bufferSize > 0)
+    *buffer = NIL;
+  return status;
+}
+
+/*************************************************************************
+ * snprintfcat
+ * Appends the new string to the buffer string overwriting the '\0'
+ * character at the end of buffer.
+ */
+int
+trio_snprintfcat(char *buffer,
+                size_t bufferSize,
+                const char *format,
+                ...)
+{
+  int status;
+  va_list args;
+  size_t buf_len;
+
+  va_start(args, format);
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  buf_len = strlen(buffer);
+  buffer = &buffer[buf_len];
+
+  status = TrioFormat(&buffer, bufferSize - 1 - buf_len,
+                     TrioOutStreamStringMax, format, args, NULL);
+  va_end(args);
+  *buffer = NIL;
+  return status;
+}
+
+int
+trio_vsnprintfcat(char *buffer,
+                 size_t bufferSize,
+                 const char *format,
+                 va_list args)
+{
+  int status;
+  size_t buf_len;
+  assert(VALID(buffer));
+  assert(VALID(format));
+
+  buf_len = strlen(buffer);
+  buffer = &buffer[buf_len];
+  status = TrioFormat(&buffer, bufferSize - 1 - buf_len,
+                     TrioOutStreamStringMax, format, args, NULL);
+  *buffer = NIL;
+  return status;
+}
+
+/*************************************************************************
+ * trio_aprintf
+ */
+
+/* Deprecated */
+char *
+trio_aprintf(const char *format,
+            ...)
+{
+  va_list args;
+  struct dynamicBuffer info;
+
+  assert(VALID(format));
+  
+  info.buffer = NULL;
+  info.length = 0;
+  info.allocated = 0;
+
+  va_start(args, format);
+  (void)TrioFormat(&info, 0, TrioOutStreamStringDynamic, format, args, NULL);
+  va_end(args);
+  if (info.length) {
+    info.buffer[info.length] = NIL; /* we terminate this with a zero byte */
+    return info.buffer;
+  }
+  else
+    return NULL;
+}
+
+/* Deprecated */
+char *
+trio_vaprintf(const char *format,
+             va_list args)
+{
+  struct dynamicBuffer info;
+
+  assert(VALID(format));
+  
+  info.buffer = NULL;
+  info.length = 0;
+  info.allocated = 0;
+
+  (void)TrioFormat(&info, 0, TrioOutStreamStringDynamic, format, args, NULL);
+  if (info.length) {
+    info.buffer[info.length] = NIL; /* we terminate this with a zero byte */
+    return info.buffer;
+  }
+  else
+    return NULL;
+}
+
+int
+trio_asprintf(char **result,
+             const char *format,
+             ...)
+{
+  va_list args;
+  int status;
+  struct dynamicBuffer info;
+
+  assert(VALID(format));
+
+  info.buffer = NULL;
+  info.length = 0;
+  info.allocated = 0;
+
+  va_start(args, format);
+  status = TrioFormat(&info, 0, TrioOutStreamStringDynamic, format, args, NULL);
+  va_end(args);
+  if (status < 0) {
+     *result = NULL;
+     return status;
+  }
+  if (info.length == 0) {
+    /*
+     * If the length is zero, no characters have been written and therefore
+     * no memory has been allocated, but we must to allocate and return an
+     * empty string.
+     */
+    info.buffer = (char *)TRIO_MALLOC(sizeof(char));
+    if (info.buffer == NULL) {
+      *result = NULL;
+      return TRIO_ERROR_RETURN(TRIO_ENOMEM, 0);
+    }
+  }
+  info.buffer[info.length] = NIL; /* we terminate this with a zero byte */
+  *result = info.buffer;
+  
+  return status;
+}
+
+int
+trio_vasprintf(char **result,
+              const char *format,
+              va_list args)
+{
+  int status;
+  struct dynamicBuffer info;
+
+  assert(VALID(format));
+
+  info.buffer = NULL;
+  info.length = 0;
+  info.allocated = 0;
+
+  status = TrioFormat(&info, 0, TrioOutStreamStringDynamic, format, args, NULL);
+  if (status < 0) {
+     *result = NULL;
+     return status;
+  }
+  if (info.length == 0) {
+    info.buffer = (char *)TRIO_MALLOC(sizeof(char));
+    if (info.buffer == NULL) {
+      *result = NULL;
+      return TRIO_ERROR_RETURN(TRIO_ENOMEM, 0);
+    }
+  }
+  info.buffer[info.length] = NIL; /* we terminate this with a zero byte */
+  *result = info.buffer;
+  
+  return status;
+}
+
+
+/*************************************************************************
+ *
+ * @CALLBACK
+ *
+ ************************************************************************/
+
+
+/*************************************************************************
+ * trio_register [public]
+ */
+void *
+trio_register(trio_callback_t callback,
+             const char *name)
+{
+  userdef_T *def;
+  userdef_T *prev = NULL;
+
+  if (callback == NULL)
+    return NULL;
+
+  if (name)
+    {
+      /* Handle built-in namespaces */
+      if (name[0] == ':')
+       {
+         if (StrEqual(name, ":enter"))
+           {
+             internalEnterCriticalRegion = callback;
+           }
+         else if (StrEqual(name, ":leave"))
+           {
+             internalLeaveCriticalRegion = callback;
+           }
+         return NULL;
+       }
+      
+      /* Bail out if namespace is too long */
+      if (StrLength(name) >= MAX_USER_NAME)
+       return NULL;
+      
+      /* Bail out if namespace already is registered */
+      def = TrioFindNamespace(name, &prev);
+      if (def)
+       return NULL;
+    }
+  
+  def = (userdef_T *)TRIO_MALLOC(sizeof(userdef_T));
+  if (def)
+    {
+      if (internalEnterCriticalRegion)
+       (void)internalEnterCriticalRegion(NULL);
+      
+      if (name)
+       {
+         /* Link into internal list */
+         if (prev == NULL)
+           internalUserDef = def;
+         else
+           prev->next = def;
+       }
+      /* Initialize */
+      def->callback = callback;
+      def->name = (name == NULL)
+       ? NULL
+       : StrDuplicate(name);
+      def->next = NULL;
+
+      if (internalLeaveCriticalRegion)
+       (void)internalLeaveCriticalRegion(NULL);
+    }
+  return def;
+}
+
+/*************************************************************************
+ * trio_unregister [public]
+ */
+void
+trio_unregister(void *handle)
+{
+  userdef_T *self = (userdef_T *)handle;
+  userdef_T *def;
+  userdef_T *prev = NULL;
+
+  assert(VALID(self));
+
+  if (self->name)
+    {
+      def = TrioFindNamespace(self->name, &prev);
+      if (def)
+       {
+         if (internalEnterCriticalRegion)
+           (void)internalEnterCriticalRegion(NULL);
+         
+         if (prev == NULL)
+           internalUserDef = NULL;
+         else
+           prev->next = def->next;
+         
+         if (internalLeaveCriticalRegion)
+           (void)internalLeaveCriticalRegion(NULL);
+       }
+      StrFree(self->name);
+    }
+  TRIO_FREE(self);
+}
+
+/*************************************************************************
+ * trio_get_format [public]
+ */
+const char *
+trio_get_format(void *ref)
+{
+  assert(((reference_T *)ref)->parameter->type == FORMAT_USER_DEFINED);
+  
+  return (((reference_T *)ref)->parameter->user_data);
+}
+
+/*************************************************************************
+ * trio_get_argument [public]
+ */
+void *
+trio_get_argument(void *ref)
+{
+  assert(((reference_T *)ref)->parameter->type == FORMAT_USER_DEFINED);
+  
+  return ((reference_T *)ref)->parameter->data.pointer;
+}
+
+/*************************************************************************
+ * trio_get_width / trio_set_width [public]
+ */
+int
+trio_get_width(void *ref)
+{
+  return ((reference_T *)ref)->parameter->width;
+}
+
+void
+trio_set_width(void *ref,
+              int width)
+{
+  ((reference_T *)ref)->parameter->width = width;
+}
+
+/*************************************************************************
+ * trio_get_precision / trio_set_precision [public]
+ */
+int
+trio_get_precision(void *ref)
+{
+  return (((reference_T *)ref)->parameter->precision);
+}
+
+void
+trio_set_precision(void *ref,
+                  int precision)
+{
+  ((reference_T *)ref)->parameter->precision = precision;
+}
+
+/*************************************************************************
+ * trio_get_base / trio_set_base [public]
+ */
+int
+trio_get_base(void *ref)
+{
+  return (((reference_T *)ref)->parameter->base);
+}
+
+void
+trio_set_base(void *ref,
+             int base)
+{
+  ((reference_T *)ref)->parameter->base = base;
+}
+
+/*************************************************************************
+ * trio_get_long / trio_set_long [public]
+ */
+int
+trio_get_long(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_LONG);
+}
+
+void
+trio_set_long(void *ref,
+             int is_long)
+{
+  if (is_long)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_LONG;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_LONG;
+}
+
+/*************************************************************************
+ * trio_get_longlong / trio_set_longlong [public]
+ */
+int
+trio_get_longlong(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_QUAD);
+}
+
+void
+trio_set_longlong(void *ref,
+                 int is_longlong)
+{
+  if (is_longlong)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_QUAD;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_QUAD;
+}
+
+/*************************************************************************
+ * trio_get_longdouble / trio_set_longdouble [public]
+ */
+int
+trio_get_longdouble(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_LONGDOUBLE);
+}
+
+void
+trio_set_longdouble(void *ref,
+                   int is_longdouble)
+{
+  if (is_longdouble)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_LONGDOUBLE;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_LONGDOUBLE;
+}
+
+/*************************************************************************
+ * trio_get_short / trio_set_short [public]
+ */
+int
+trio_get_short(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_SHORT);
+}
+
+void
+trio_set_short(void *ref,
+              int is_short)
+{
+  if (is_short)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_SHORT;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_SHORT;
+}
+
+/*************************************************************************
+ * trio_get_shortshort / trio_set_shortshort [public]
+ */
+int
+trio_get_shortshort(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_SHORTSHORT);
+}
+
+void
+trio_set_shortshort(void *ref,
+              int is_shortshort)
+{
+  if (is_shortshort)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_SHORTSHORT;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_SHORTSHORT;
+}
+
+/*************************************************************************
+ * trio_get_alternative / trio_set_alternative [public]
+ */
+int
+trio_get_alternative(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_ALTERNATIVE);
+}
+
+void
+trio_set_alternative(void *ref,
+                    int is_alternative)
+{
+  if (is_alternative)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_ALTERNATIVE;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_ALTERNATIVE;
+}
+
+/*************************************************************************
+ * trio_get_alignment / trio_set_alignment [public]
+ */
+int
+trio_get_alignment(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_LEFTADJUST);
+}
+
+void
+trio_set_alignment(void *ref,
+                  int is_leftaligned)
+{
+  if (is_leftaligned)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_LEFTADJUST;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_LEFTADJUST;
+}
+
+/*************************************************************************
+ * trio_get_spacing /trio_set_spacing [public]
+ */
+int
+trio_get_spacing(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_SPACE);
+}
+
+void
+trio_set_spacing(void *ref,
+                int is_space)
+{
+  if (is_space)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_SPACE;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_SPACE;
+}
+
+/*************************************************************************
+ * trio_get_sign / trio_set_sign [public]
+ */
+int
+trio_get_sign(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_SHOWSIGN);
+}
+
+void
+trio_set_sign(void *ref,
+             int is_sign)
+{
+  if (is_sign)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_SHOWSIGN;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_SHOWSIGN;
+}
+
+/*************************************************************************
+ * trio_get_padding / trio_set_padding [public]
+ */
+int
+trio_get_padding(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_NILPADDING);
+}
+
+void
+trio_set_padding(void *ref,
+                int is_padding)
+{
+  if (is_padding)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_NILPADDING;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_NILPADDING;
+}
+
+/*************************************************************************
+ * trio_get_quote / trio_set_quote [public]
+ */
+int
+trio_get_quote(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_QUOTE);
+}
+
+void
+trio_set_quote(void *ref,
+              int is_quote)
+{
+  if (is_quote)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_QUOTE;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_QUOTE;
+}
+
+/*************************************************************************
+ * trio_get_upper / trio_set_upper [public]
+ */
+int
+trio_get_upper(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_UPPER);
+}
+
+void
+trio_set_upper(void *ref,
+              int is_upper)
+{
+  if (is_upper)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_UPPER;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_UPPER;
+}
+
+/*************************************************************************
+ * trio_get_largest / trio_set_largest [public]
+ */
+#if TRIO_C99
+int
+trio_get_largest(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_INTMAX_T);
+}
+
+void
+trio_set_largest(void *ref,
+                int is_largest)
+{
+  if (is_largest)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_INTMAX_T;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_INTMAX_T;
+}
+#endif
+
+/*************************************************************************
+ * trio_get_ptrdiff / trio_set_ptrdiff [public]
+ */
+int
+trio_get_ptrdiff(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_PTRDIFF_T);
+}
+
+void
+trio_set_ptrdiff(void *ref,
+                int is_ptrdiff)
+{
+  if (is_ptrdiff)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_PTRDIFF_T;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_PTRDIFF_T;
+}
+
+/*************************************************************************
+ * trio_get_size / trio_set_size [public]
+ */
+#if TRIO_C99
+int
+trio_get_size(void *ref)
+{
+  return (((reference_T *)ref)->parameter->flags & FLAGS_SIZE_T);
+}
+
+void
+trio_set_size(void *ref,
+             int is_size)
+{
+  if (is_size)
+    ((reference_T *)ref)->parameter->flags |= FLAGS_SIZE_T;
+  else
+    ((reference_T *)ref)->parameter->flags &= ~FLAGS_SIZE_T;
+}
+#endif
+
+/*************************************************************************
+ * trio_print_int [public]
+ */
+void
+trio_print_int(void *ref,
+              int number)
+{
+  reference_T *self = (reference_T *)ref;
+
+  TrioWriteNumber(self->data,
+                 (trio_intmax_t)number,
+                 self->parameter->flags,
+                 self->parameter->width,
+                 self->parameter->precision,
+                 self->parameter->base);
+}
+
+/*************************************************************************
+ * trio_print_uint [public]
+ */
+void
+trio_print_uint(void *ref,
+               unsigned int number)
+{
+  reference_T *self = (reference_T *)ref;
+
+  TrioWriteNumber(self->data,
+                 (trio_intmax_t)number,
+                 self->parameter->flags | FLAGS_UNSIGNED,
+                 self->parameter->width,
+                 self->parameter->precision,
+                 self->parameter->base);
+}
+
+/*************************************************************************
+ * trio_print_double [public]
+ */
+void
+trio_print_double(void *ref,
+                 double number)
+{
+  reference_T *self = (reference_T *)ref;
+
+  TrioWriteDouble(self->data,
+                 number,
+                 self->parameter->flags,
+                 self->parameter->width,
+                 self->parameter->precision,
+                 self->parameter->base);
+}
+
+/*************************************************************************
+ * trio_print_string [public]
+ */
+void
+trio_print_string(void *ref,
+                 char *string)
+{
+  reference_T *self = (reference_T *)ref;
+
+  TrioWriteString(self->data,
+                 string,
+                 self->parameter->flags,
+                 self->parameter->width,
+                 self->parameter->precision);
+}
+
+/*************************************************************************
+ * trio_print_pointer [public]
+ */
+void
+trio_print_pointer(void *ref,
+                  void *pointer)
+{
+  reference_T *self = (reference_T *)ref;
+  unsigned long flags;
+  trio_uintmax_t number;
+
+  if (NULL == pointer)
+    {
+      const char *string = null;
+      while (*string)
+       self->data->OutStream(self->data, *string++);
+    }
+  else
+    {
+      /*
+       * The subtraction of the null pointer is a workaround
+       * to avoid a compiler warning. The performance overhead
+       * is negligible (and likely to be removed by an
+       * optimising compiler). The (char *) casting is done
+       * to please ANSI C++.
+       */
+      number = (trio_uintmax_t)((char *)pointer - (char *)0);
+      /* Shrink to size of pointer */
+      number &= (trio_uintmax_t)-1;
+      flags = self->parameter->flags;
+      flags |= (FLAGS_UNSIGNED | FLAGS_ALTERNATIVE |
+               FLAGS_NILPADDING);
+      TrioWriteNumber(self->data,
+                     (trio_intmax_t)number,
+                     flags,
+                     POINTER_WIDTH,
+                     NO_PRECISION,
+                     BASE_HEX);
+    }
+}
+
+/*************************************************************************
+ * trio_print_ref [public]
+ */
+int
+trio_print_ref(void *ref,
+              const char *format,
+              ...)
+{
+  int status;
+  va_list arglist;
+
+  assert(VALID(format));
+  
+  va_start(arglist, format);
+  status = TrioFormatRef((reference_T *)ref, format, arglist, NULL);
+  va_end(arglist);
+  return status;
+}
+
+/*************************************************************************
+ * trio_vprint_ref [public]
+ */
+int
+trio_vprint_ref(void *ref,
+               const char *format,
+               va_list arglist)
+{
+  assert(VALID(format));
+  
+  return TrioFormatRef((reference_T *)ref, format, arglist, NULL);
+}
+
+/*************************************************************************
+ * trio_printv_ref [public]
+ */
+int
+trio_printv_ref(void *ref,
+               const char *format,
+               void **argarray)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(format));
+  
+  return TrioFormatRef((reference_T *)ref, format, dummy, argarray);
+}
+
+
+/*************************************************************************
+ *
+ * @SCANNING
+ *
+ ************************************************************************/
+
+
+/*************************************************************************
+ * TrioSkipWhitespaces [private]
+ */
+static int
+TrioSkipWhitespaces(trio_T *self)
+{
+  int ch;
+
+  ch = self->current;
+  while (isspace(ch))
+    {
+      self->InStream(self, &ch);
+    }
+  return ch;
+}
+
+/*************************************************************************
+ * TrioGetCollation [private]
+ */
+#if TRIO_EXTENSION
+static void
+TrioGetCollation()
+{
+  int i;
+  int j;
+  int k;
+  char first[2];
+  char second[2];
+
+  /* This is computational expensive */
+  first[1] = NIL;
+  second[1] = NIL;
+  for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+    {
+      k = 0;
+      first[0] = (char)i;
+      for (j = 0; j < MAX_CHARACTER_CLASS; j++)
+       {
+         second[0] = (char)j;
+         if (StrEqualLocale(first, second))
+           internalCollationArray[i][k++] = (char)j;
+       }
+      internalCollationArray[i][k] = NIL;
+    }
+}
+#endif
+
+/*************************************************************************
+ * TrioGetCharacterClass [private]
+ *
+ * FIXME:
+ *  multibyte
+ */
+static int
+TrioGetCharacterClass(const char *format,
+                     int *indexPointer,
+                     unsigned long *flagsPointer,
+                     int *characterclass)
+{
+  int index = *indexPointer;
+  int i;
+  char ch;
+  char range_begin;
+  char range_end;
+
+  *flagsPointer &= ~FLAGS_EXCLUDE;
+
+  if (format[index] == QUALIFIER_CIRCUMFLEX)
+    {
+      *flagsPointer |= FLAGS_EXCLUDE;
+      index++;
+    }
+  /*
+   * If the ungroup character is at the beginning of the scanlist,
+   * it will be part of the class, and a second ungroup character
+   * must follow to end the group.
+   */
+  if (format[index] == SPECIFIER_UNGROUP)
+    {
+      characterclass[(int)SPECIFIER_UNGROUP]++;
+      index++;
+    }
+  /*
+   * Minus is used to specify ranges. To include minus in the class,
+   * it must be at the beginning of the list
+   */
+  if (format[index] == QUALIFIER_MINUS)
+    {
+      characterclass[(int)QUALIFIER_MINUS]++;
+      index++;
+    }
+  /* Collect characters */
+  for (ch = format[index];
+       (ch != SPECIFIER_UNGROUP) && (ch != NIL);
+       ch = format[++index])
+    {
+      switch (ch)
+       {
+       case QUALIFIER_MINUS: /* Scanlist ranges */
+         
+         /*
+          * Both C99 and UNIX98 describes ranges as implementation-
+          * defined.
+          *
+          * We support the following behaviour (although this may
+          * change as we become wiser)
+          * - only increasing ranges, ie. [a-b] but not [b-a]
+          * - transitive ranges, ie. [a-b-c] == [a-c]
+          * - trailing minus, ie. [a-] is interpreted as an 'a'
+          *   and a '-'
+          * - duplicates (although we can easily convert these
+          *   into errors)
+          */
+         range_begin = format[index - 1];
+         range_end = format[++index];
+         if (range_end == SPECIFIER_UNGROUP)
+           {
+             /* Trailing minus is included */
+             characterclass[(int)ch]++;
+             ch = range_end;
+             break; /* for */
+           }
+         if (range_end == NIL)
+           return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+         if (range_begin > range_end)
+           return TRIO_ERROR_RETURN(TRIO_ERANGE, index);
+           
+         for (i = (int)range_begin; i <= (int)range_end; i++)
+           characterclass[i]++;
+           
+         ch = range_end;
+         break;
+         
+#if TRIO_EXTENSION
+
+       case SPECIFIER_GROUP:
+         
+         switch (format[index + 1])
+           {
+           case QUALIFIER_DOT: /* Collating symbol */
+             /*
+              * FIXME: This will be easier to implement when multibyte
+              * characters have been implemented. Until now, we ignore
+              * this feature.
+              */
+             for (i = index + 2; ; i++)
+               {
+                 if (format[i] == NIL)
+                   /* Error in syntax */
+                   return -1;
+                 else if (format[i] == QUALIFIER_DOT)
+                   break; /* for */
+               }
+             if (format[++i] != SPECIFIER_UNGROUP)
+               return -1;
+             
+             index = i;
+             break;
+         
+           case QUALIFIER_EQUAL: /* Equivalence class expressions */
+             {
+               unsigned int j;
+               unsigned int k;
+           
+               if (internalCollationUnconverted)
+                 {
+                   /* Lazy evalutation of collation array */
+                   TrioGetCollation();
+                   internalCollationUnconverted = FALSE;
+                 }
+               for (i = index + 2; ; i++)
+                 {
+                   if (format[i] == NIL)
+                     /* Error in syntax */
+                     return -1;
+                   else if (format[i] == QUALIFIER_EQUAL)
+                     break; /* for */
+                   else
+                     {
+                       /* Mark any equivalent character */
+                       k = (unsigned int)format[i];
+                       for (j = 0; internalCollationArray[k][j] != NIL; j++)
+                         characterclass[(int)internalCollationArray[k][j]]++;
+                     }
+                 }
+               if (format[++i] != SPECIFIER_UNGROUP)
+                 return -1;
+               
+               index = i;
+             }
+             break;
+         
+           case QUALIFIER_COLON: /* Character class expressions */
+         
+             if (StrEqualMax(CLASS_ALNUM, sizeof(CLASS_ALNUM) - 1,
+                             &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isalnum(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_ALNUM) - 1;
+               }
+             else if (StrEqualMax(CLASS_ALPHA, sizeof(CLASS_ALPHA) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isalpha(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_ALPHA) - 1;
+               }
+             else if (StrEqualMax(CLASS_CNTRL, sizeof(CLASS_CNTRL) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (iscntrl(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_CNTRL) - 1;
+               }
+             else if (StrEqualMax(CLASS_DIGIT, sizeof(CLASS_DIGIT) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isdigit(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_DIGIT) - 1;
+               }
+             else if (StrEqualMax(CLASS_GRAPH, sizeof(CLASS_GRAPH) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isgraph(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_GRAPH) - 1;
+               }
+             else if (StrEqualMax(CLASS_LOWER, sizeof(CLASS_LOWER) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (islower(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_LOWER) - 1;
+               }
+             else if (StrEqualMax(CLASS_PRINT, sizeof(CLASS_PRINT) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isprint(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_PRINT) - 1;
+               }
+             else if (StrEqualMax(CLASS_PUNCT, sizeof(CLASS_PUNCT) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (ispunct(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_PUNCT) - 1;
+               }
+             else if (StrEqualMax(CLASS_SPACE, sizeof(CLASS_SPACE) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isspace(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_SPACE) - 1;
+               }
+             else if (StrEqualMax(CLASS_UPPER, sizeof(CLASS_UPPER) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isupper(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_UPPER) - 1;
+               }
+             else if (StrEqualMax(CLASS_XDIGIT, sizeof(CLASS_XDIGIT) - 1,
+                                  &format[index]))
+               {
+                 for (i = 0; i < MAX_CHARACTER_CLASS; i++)
+                   if (isxdigit(i))
+                     characterclass[i]++;
+                 index += sizeof(CLASS_XDIGIT) - 1;
+               }
+             else
+               {
+                 characterclass[(int)ch]++;
+               }
+             break;
+
+           default:
+             characterclass[(int)ch]++;
+             break;
+           }
+         break;
+         
+#endif /* TRIO_EXTENSION */
+         
+       default:
+         characterclass[(int)ch]++;
+         break;
+       }
+    }
+  return 0;
+}
+
+/*************************************************************************
+ * TrioReadNumber [private]
+ *
+ * We implement our own number conversion in preference of strtol and
+ * strtoul, because we must handle 'long long' and thousand separators.
+ */
+static BOOLEAN_T
+TrioReadNumber(trio_T *self,
+              trio_uintmax_t *target,
+              unsigned long flags,
+              int width,
+              int base)
+{
+  trio_uintmax_t number = 0;
+  int digit;
+  int count;
+  BOOLEAN_T isNegative = FALSE;
+  int j;
+
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+  assert((base >= MIN_BASE && base <= MAX_BASE) || (base == NO_BASE));
+
+  if (internalDigitsUnconverted)
+    {
+      /* Lazy evaluation of digits array */
+      memset(internalDigitArray, -1, sizeof(internalDigitArray));
+      for (j = 0; j < (int)sizeof(internalDigitsLower) - 1; j++)
+       {
+         internalDigitArray[(int)internalDigitsLower[j]] = j;
+         internalDigitArray[(int)internalDigitsUpper[j]] = j;
+       }
+      internalDigitsUnconverted = FALSE;
+    }
+  
+  TrioSkipWhitespaces(self);
+  
+  if (!(flags & FLAGS_UNSIGNED))
+    {
+      /* Leading sign */
+      if (self->current == '+')
+       {
+         self->InStream(self, NULL);
+       }
+      else if (self->current == '-')
+       {
+         self->InStream(self, NULL);
+         isNegative = TRUE;
+       }
+    }
+  
+  count = self->processed;
+  
+  if (flags & FLAGS_ALTERNATIVE)
+    {
+      switch (base)
+       {
+       case NO_BASE:
+       case BASE_OCTAL:
+       case BASE_HEX:
+       case BASE_BINARY:
+         if (self->current == '0')
+           {
+             self->InStream(self, NULL);
+             if (self->current)
+               {
+                 if ((base == BASE_HEX) &&
+                     (toupper(self->current) == 'X'))
+                   {
+                     self->InStream(self, NULL);
+                   }
+                 else if ((base == BASE_BINARY) &&
+                          (toupper(self->current) == 'B'))
+                   {
+                     self->InStream(self, NULL);
+                   }
+               }
+           }
+         else
+           return FALSE;
+         break;
+       default:
+         break;
+       }
+    }
+
+  while (((width == NO_WIDTH) || (self->processed - count < width)) &&
+        (! ((self->current == EOF) || isspace(self->current))))
+    {
+      if (isascii(self->current))
+       {
+         digit = internalDigitArray[self->current];
+         /* Abort if digit is not allowed in the specified base */
+         if ((digit == -1) || (digit >= base))
+           break;
+       }
+      else if (flags & FLAGS_QUOTE)
+       {
+         /* Compare with thousands separator */
+         for (j = 0; internalThousandSeparator[j] && self->current; j++)
+           {
+             if (internalThousandSeparator[j] != self->current)
+               break;
+
+             self->InStream(self, NULL);
+           }
+         if (internalThousandSeparator[j])
+           break; /* Mismatch */
+         else
+           continue; /* Match */
+       }
+      else
+       break;
+            
+      number *= base;
+      number += digit;
+
+      self->InStream(self, NULL);
+    }
+
+  /* Was anything read at all? */
+  if (self->processed == count)
+    return FALSE;
+  
+  if (target)
+    *target = (isNegative) ? -number : number;
+  return TRUE;
+}
+
+/*************************************************************************
+ * TrioReadChar [private]
+ */
+static int
+TrioReadChar(trio_T *self,
+            char *target,
+            unsigned long flags,
+            int width)
+{
+  int i;
+  char ch;
+  trio_uintmax_t number;
+  
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+
+  for (i = 0;
+       (self->current != EOF) && (i < width);
+       i++)
+    {
+      ch = (char)self->current;
+      self->InStream(self, NULL);
+      if ((flags & FLAGS_ALTERNATIVE) && (ch == CHAR_BACKSLASH))
+       {
+         switch (self->current)
+           {
+           case '\\': ch = '\\'; break;
+           case 'a': ch = '\007'; break;
+           case 'b': ch = '\b'; break;
+           case 'f': ch = '\f'; break;
+           case 'n': ch = '\n'; break;
+           case 'r': ch = '\r'; break;
+           case 't': ch = '\t'; break;
+           case 'v': ch = '\v'; break;
+           default:
+             if (isdigit(self->current))
+               {
+                 /* Read octal number */
+                 if (!TrioReadNumber(self, &number, 0, 3, BASE_OCTAL))
+                   return 0;
+                 ch = (char)number;
+               }
+             else if (toupper(self->current) == 'X')
+               {
+                 /* Read hexadecimal number */
+                 self->InStream(self, NULL);
+                 if (!TrioReadNumber(self, &number, 0, 2, BASE_HEX))
+                   return 0;
+                 ch = (char)number;
+               }
+             else
+               {
+                 ch = (char)self->current;
+               }
+             break;
+           }
+       }
+      
+      if (target)
+       target[i] = ch;
+    }
+  return i + 1;
+}
+
+/*************************************************************************
+ * TrioReadString [private]
+ */
+static BOOLEAN_T
+TrioReadString(trio_T *self,
+              char *target,
+              unsigned long flags,
+              int width)
+{
+  int i;
+  
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+
+  TrioSkipWhitespaces(self);
+    
+  /*
+   * Continue until end of string is reached, a whitespace is encountered,
+   * or width is exceeded
+   */
+  for (i = 0;
+       ((width == NO_WIDTH) || (i < width)) &&
+       (! ((self->current == EOF) || isspace(self->current)));
+       i++)
+    {
+      if (TrioReadChar(self, &target[i], flags, 1) == 0)
+       break; /* for */
+    }
+  if (target)
+    target[i] = NIL;
+  return TRUE;
+}
+
+/*************************************************************************
+ * TrioReadWideChar [private]
+ */
+#if TRIO_WIDECHAR
+static int
+TrioReadWideChar(trio_T *self,
+                wchar_t *target,
+                unsigned long flags,
+                int width)
+{
+  int i;
+  int j;
+  int size;
+  int amount = 0;
+  wchar_t wch;
+  char buffer[MB_LEN_MAX + 1];
+  
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+
+  for (i = 0;
+       (self->current != EOF) && (i < width);
+       i++)
+    {
+      if (isascii(self->current))
+       {
+         if (TrioReadChar(self, buffer, flags, 1) == 0)
+           return 0;
+         buffer[1] = NIL;
+       }
+      else
+       {
+         /*
+          * Collect a multibyte character, by enlarging buffer until
+          * it contains a fully legal multibyte character, or the
+          * buffer is full.
+          */
+         j = 0;
+         do
+           {
+             buffer[j++] = (char)self->current;
+             buffer[j] = NIL;
+             self->InStream(self, NULL);
+           }
+         while ((j < (int)sizeof(buffer)) && (mblen(buffer, (size_t)j) != j));
+       }
+      if (target)
+       {
+         size = mbtowc(&wch, buffer, sizeof(buffer));
+         if (size > 0)
+           target[i] = wch;
+       }
+      amount += size;
+      self->InStream(self, NULL);
+    }
+  return amount;
+}
+#endif /* TRIO_WIDECHAR */
+
+/*************************************************************************
+ * TrioReadWideString [private]
+ */
+#if TRIO_WIDECHAR
+static BOOLEAN_T
+TrioReadWideString(trio_T *self,
+                  wchar_t *target,
+                  unsigned long flags,
+                  int width)
+{
+  int i;
+  int size;
+  
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+
+  TrioSkipWhitespaces(self);
+
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  mblen(NULL, 0);
+#endif
+  
+  /*
+   * Continue until end of string is reached, a whitespace is encountered,
+   * or width is exceeded
+   */
+  for (i = 0;
+       ((width == NO_WIDTH) || (i < width)) &&
+       (! ((self->current == EOF) || isspace(self->current)));
+       )
+    {
+      size = TrioReadWideChar(self, &target[i], flags, 1);
+      if (size == 0)
+       break; /* for */
+
+      i += size;
+    }
+  if (target)
+    target[i] = L'\0';
+  return TRUE;
+}
+#endif /* TRIO_WIDECHAR */
+
+/*************************************************************************
+ * TrioReadGroup [private]
+ *
+ * FIXME: characterclass does not work with multibyte characters
+ */
+static BOOLEAN_T
+TrioReadGroup(trio_T *self,
+             char *target,
+             int *characterclass,
+             unsigned long flags,
+             int width)
+{
+  int ch;
+  int i;
+  
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+
+  ch = self->current;
+  for (i = 0;
+       ((width == NO_WIDTH) || (i < width)) &&
+       (! ((ch == EOF) ||
+          (((flags & FLAGS_EXCLUDE) != 0) ^ (characterclass[ch] == 0))));
+       i++)
+    {
+      if (target)
+       target[i] = (char)ch;
+      self->InStream(self, &ch);
+    }
+  
+  if (target)
+    target[i] = NIL;
+  return TRUE;
+}
+
+/*************************************************************************
+ * TrioReadDouble [private]
+ *
+ * FIXME:
+ *  add long double
+ */
+static BOOLEAN_T
+TrioReadDouble(trio_T *self,
+              double *target,
+              unsigned long flags,
+              int width)
+{
+  int ch;
+  char doubleString[512] = "";
+  int index = 0;
+  int start;
+  int j;
+  BOOLEAN_T isHex = FALSE;
+
+  if ((width == NO_WIDTH) || (width > (int)sizeof(doubleString) - 1))
+    width = sizeof(doubleString) - 1;
+  
+  TrioSkipWhitespaces(self);
+  
+  /*
+   * Read entire double number from stream. StrToDouble requires a
+   * string as input, but InStream can be anything, so we have to
+   * collect all characters.
+   */
+  ch = self->current;
+  if ((ch == '+') || (ch == '-'))
+    {
+      doubleString[index++] = (char)ch;
+      self->InStream(self, &ch);
+      width--;
+    }
+
+  start = index;
+  switch (ch)
+    {
+    case 'n':
+    case 'N':
+      /* Not-a-number */
+      if (index != 0)
+       break;
+      /* FALLTHROUGH */
+    case 'i':
+    case 'I':
+      /* Infinity */
+      while (isalpha(ch) && (index - start < width))
+       {
+         doubleString[index++] = (char)ch;
+         self->InStream(self, &ch);
+       }
+      doubleString[index] = NIL;
+
+      /* Case insensitive string comparison */
+      if (StrEqual(&doubleString[start], INFINITE_UPPER) ||
+         StrEqual(&doubleString[start], LONG_INFINITE_UPPER))
+       {
+         *target = ((start == 1 && doubleString[0] == '-'))
+           ? -HUGE_VAL
+           : HUGE_VAL;
+         return TRUE;
+       }
+      if (StrEqual(doubleString, NAN_LOWER))
+       {
+         /* NaN must not have a preceeding + nor - */
+         *target = TrioGenerateNaN();
+         return TRUE;
+       }
+      return FALSE;
+      
+    default:
+      break;
+    }
+  
+  if (ch == '0')
+    {
+      doubleString[index++] = (char)ch;
+      self->InStream(self, &ch);
+      if (toupper(ch) == 'X')
+       {
+         isHex = TRUE;
+         doubleString[index++] = (char)ch;
+         self->InStream(self, &ch);
+       }
+    }
+  while ((ch != EOF) && (index - start < width))
+    {
+      /* Integer part */
+      if (isHex ? isxdigit(ch) : isdigit(ch))
+       {
+         doubleString[index++] = (char)ch;
+         self->InStream(self, &ch);
+       }
+      else if (flags & FLAGS_QUOTE)
+       {
+         /* Compare with thousands separator */
+         for (j = 0; internalThousandSeparator[j] && self->current; j++)
+           {
+             if (internalThousandSeparator[j] != self->current)
+               break;
+
+             self->InStream(self, &ch);
+           }
+         if (internalThousandSeparator[j])
+           break; /* Mismatch */
+         else
+           continue; /* Match */
+       }
+      else
+       break; /* while */
+    }
+  if (ch == '.')
+    {
+      /* Decimal part */
+      doubleString[index++] = (char)ch;
+      self->InStream(self, &ch);
+      while ((isHex ? isxdigit(ch) : isdigit(ch)) &&
+            (index - start < width))
+       {
+         doubleString[index++] = (char)ch;
+         self->InStream(self, &ch);
+       }
+      if (isHex ? (toupper(ch) == 'P') : (toupper(ch) == 'E'))
+       {
+         /* Exponent */
+         doubleString[index++] = (char)ch;
+         self->InStream(self, &ch);
+         if ((ch == '+') || (ch == '-'))
+           {
+             doubleString[index++] = (char)ch;
+             self->InStream(self, &ch);
+           }
+         while ((isHex ? isxdigit(ch) : isdigit(ch)) &&
+                (index - start < width))
+           {
+             doubleString[index++] = (char)ch;
+             self->InStream(self, &ch);
+           }
+       }
+    }
+
+  if ((index == start) || (*doubleString == NIL))
+    return FALSE;
+  
+  if (flags & FLAGS_LONGDOUBLE)
+/*     *longdoublePointer = StrToLongDouble()*/
+    return FALSE; /* FIXME: Remove when long double is implemented */
+  else
+    {
+      *target = StrToDouble(doubleString, NULL);
+    }
+  return TRUE;
+}
+
+/*************************************************************************
+ * TrioReadPointer [private]
+ */
+static BOOLEAN_T
+TrioReadPointer(trio_T *self,
+               void **target,
+               unsigned long flags)
+{
+  trio_uintmax_t number;
+  char buffer[sizeof(null)];
+
+  flags |= (FLAGS_UNSIGNED | FLAGS_ALTERNATIVE | FLAGS_NILPADDING);
+  
+  if (TrioReadNumber(self,
+                    &number,
+                    flags,
+                    POINTER_WIDTH,
+                    BASE_HEX))
+    {
+      /*
+       * The strange assignment of number is a workaround for a compiler
+       * warning
+       */
+      if (target)
+       *target = (char *)0 + number;
+      return TRUE;
+    }
+  else if (TrioReadString(self,
+                         (flags & FLAGS_IGNORE)
+                         ? NULL
+                         : buffer,
+                         0,
+                         sizeof(null) - 1))
+    {  
+      if (StrEqualCase(buffer, null))
+       {
+         if (target)
+           *target = NULL;
+         return TRUE;
+       }
+    }
+  return FALSE;
+}
+
+/*************************************************************************
+ * TrioScan [private]
+ */
+static int
+TrioScan(const void *source,
+        size_t sourceSize,
+        void (*InStream)(trio_T *, int *),
+        const char *format,
+        va_list arglist,
+        void **argarray)
+{
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  int charlen;
+#endif
+  int status;
+  int assignment;
+  parameter_T parameters[MAX_PARAMETERS];
+  trio_T internalData;
+  trio_T *data;
+  int ch;
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  int cnt;
+#endif
+  int index; /* Index of format string */
+  int i; /* Index of current parameter */
+  unsigned long flags;
+  int width;
+  int base;
+  void *pointer;
+
+  assert(VALID(InStream));
+  assert(VALID(format));
+
+  memset(&internalData, 0, sizeof(internalData));
+  data = &internalData;
+  data->InStream = InStream;
+  data->location = (void *)source;
+  data->max = sourceSize;
+
+#if defined(USE_LOCALE)
+  if (NULL == internalLocaleValues)
+    {
+      TrioSetLocale();
+    }
+#endif
+  
+  status = TrioPreprocess(TYPE_SCAN, format, parameters, arglist, argarray);
+  if (status < 0)
+    return status;
+
+  assignment = 0;
+  i = 0;
+  index = 0;
+  data->InStream(data, &ch);
+
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+  mblen(NULL, 0);
+#endif
+
+  while (format[index])
+    {
+#if defined(TRIO_COMPILER_SUPPORTS_MULTIBYTE)
+      if (! isascii(format[index]))
+       {
+         charlen = mblen(&format[index], MB_LEN_MAX);
+         /* Compare multibyte characters in format string */
+         for (cnt = 0; cnt < charlen - 1; cnt++)
+           {
+             if (ch != format[index + cnt])
+               {
+                 return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+               }
+             data->InStream(data, &ch);
+           }
+         continue; /* while */
+       }
+#endif /* TRIO_COMPILER_SUPPORTS_MULTIBYTE */
+      if (EOF == ch)
+       return EOF;
+      
+      if (CHAR_IDENTIFIER == format[index])
+       {
+         if (CHAR_IDENTIFIER == format[index + 1])
+           {
+             /* Two % in format matches one % in input stream */
+             if (CHAR_IDENTIFIER == ch)
+               {
+                 data->InStream(data, &ch);
+                 index += 2;
+                 continue; /* while format chars left */
+               }
+             else
+               return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+           }
+
+         /* Skip the parameter entries */
+         while (parameters[i].type == FORMAT_PARAMETER)
+           i++;
+         
+         flags = parameters[i].flags;
+         /* Find width */
+         width = parameters[i].width;
+         if (flags & FLAGS_WIDTH_PARAMETER)
+           {
+             /* Get width from parameter list */
+             width = (int)parameters[width].data.number.as_signed;
+           }
+         /* Find base */
+         base = parameters[i].base;
+         if (flags & FLAGS_BASE_PARAMETER)
+           {
+             /* Get base from parameter list */
+             base = (int)parameters[base].data.number.as_signed;
+           }
+         
+         switch (parameters[i].type)
+           {
+           case FORMAT_INT:
+             {
+               trio_uintmax_t number;
+
+               if (0 == base)
+                 base = BASE_DECIMAL;
+
+               if (!TrioReadNumber(data,
+                                   &number,
+                                   flags,
+                                   width,
+                                   base))
+                 return assignment;
+               assignment++;
+               
+               if (!(flags & FLAGS_IGNORE))
+                 {
+                   pointer = parameters[i].data.pointer;
+#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
+                   if (flags & FLAGS_SIZE_T)
+                     *(size_t *)pointer = (size_t)number;
+                   else
+#endif
+#if defined(QUALIFIER_PTRDIFF_T)
+                   if (flags & FLAGS_PTRDIFF_T)
+                     *(ptrdiff_t *)pointer = (ptrdiff_t)number;
+                   else
+#endif
+#if defined(QUALIFIER_INTMAX_T)
+                   if (flags & FLAGS_INTMAX_T)
+                     *(trio_intmax_t *)pointer = (trio_intmax_t)number;
+                   else
+#endif
+                   if (flags & FLAGS_QUAD)
+                     *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)number;
+                   else if (flags & FLAGS_LONG)
+                     *(long int *)pointer = (long int)number;
+                   else if (flags & FLAGS_SHORT)
+                     *(short int *)pointer = (short int)number;
+                   else
+                     *(int *)pointer = (int)number;
+                 }
+             }
+             break; /* FORMAT_INT */
+             
+           case FORMAT_STRING:
+#if TRIO_WIDECHAR
+             if (flags & FLAGS_WIDECHAR)
+               {
+                 if (!TrioReadWideString(data,
+                                         (flags & FLAGS_IGNORE)
+                                         ? NULL
+                                         : parameters[i].data.wstring,
+                                         flags,
+                                         width))
+                   return assignment;
+               }
+             else
+#endif
+               {
+                 if (!TrioReadString(data,
+                                     (flags & FLAGS_IGNORE)
+                                     ? NULL
+                                     : parameters[i].data.string,
+                                     flags,
+                                     width))
+                   return assignment;
+               }
+             assignment++;
+             break; /* FORMAT_STRING */
+             
+           case FORMAT_DOUBLE:
+             if (!TrioReadDouble(data,
+                                 (flags & FLAGS_IGNORE)
+                                 ? NULL
+                                 : parameters[i].data.doublePointer,
+                                 flags,
+                                 width))
+               return assignment;
+             assignment++;
+             break; /* FORMAT_DOUBLE */
+
+           case FORMAT_GROUP:
+             {
+               int characterclass[MAX_CHARACTER_CLASS + 1];
+               int rc;
+
+               /* Skip over modifiers */
+               while (format[index] != SPECIFIER_GROUP)
+                 {
+                   index++;
+                 }
+               /* Skip over group specifier */
+               index++;
+               
+               memset(characterclass, 0, sizeof(characterclass));
+               rc = TrioGetCharacterClass(format,
+                                          &index,
+                                          &flags,
+                                          characterclass);
+               if (rc < 0)
+                 return rc;
+
+               if (!TrioReadGroup(data,
+                                  (flags & FLAGS_IGNORE)
+                                  ? NULL
+                                  : parameters[i].data.string,
+                                  characterclass,
+                                  flags,
+                                  parameters[i].width))
+                 return assignment;
+               assignment++;
+             }
+             break; /* FORMAT_GROUP */
+             
+           case FORMAT_COUNT:
+             pointer = parameters[i].data.pointer;
+             if (NULL != pointer)
+               {
+#if defined(QUALIFIER_SIZE_T) || defined(QUALIFIER_SIZE_T_UPPER)
+                 if (flags & FLAGS_SIZE_T)
+                   *(size_t *)pointer = (size_t)data->committed;
+                 else
+#endif
+#if defined(QUALIFIER_PTRDIFF_T)
+                 if (flags & FLAGS_PTRDIFF_T)
+                   *(ptrdiff_t *)pointer = (ptrdiff_t)data->committed;
+                 else
+#endif
+#if defined(QUALIFIER_INTMAX_T)
+                 if (flags & FLAGS_INTMAX_T)
+                   *(trio_intmax_t *)pointer = (trio_intmax_t)data->committed;
+                 else
+#endif
+                 if (flags & FLAGS_QUAD)
+                   {
+                     *(trio_ulonglong_t *)pointer = (trio_ulonglong_t)data->committed;
+                   }
+                 else if (flags & FLAGS_LONG)
+                   {
+                     *(long int *)pointer = (long int)data->committed;
+                   }
+                 else if (flags & FLAGS_SHORT)
+                   {
+                     *(short int *)pointer = (short int)data->committed;
+                   }
+                 else
+                   {
+                     *(int *)pointer = (int)data->committed;
+                   }
+               }
+             break; /* FORMAT_COUNT */
+             
+           case FORMAT_CHAR:
+#if TRIO_WIDECHAR
+             if (flags & FLAGS_WIDECHAR)
+               {
+                 if (TrioReadWideChar(data,
+                                      (flags & FLAGS_IGNORE)
+                                      ? NULL
+                                      : parameters[i].data.wstring,
+                                      flags,
+                                      (width == NO_WIDTH) ? 1 : width) > 0)
+                   return assignment;
+               }
+             else
+#endif
+               {
+                 if (TrioReadChar(data,
+                                  (flags & FLAGS_IGNORE)
+                                  ? NULL
+                                  : parameters[i].data.string,
+                                  flags,
+                                  (width == NO_WIDTH) ? 1 : width) > 0)
+                   return assignment;
+               }
+             assignment++;
+             break; /* FORMAT_CHAR */
+             
+           case FORMAT_POINTER:
+             if (!TrioReadPointer(data,
+                                  (flags & FLAGS_IGNORE)
+                                  ? NULL
+                                  : (void **)parameters[i].data.pointer,
+                                  flags))
+               return assignment;
+             assignment++;
+             break; /* FORMAT_POINTER */
+             
+           case FORMAT_PARAMETER:
+             break; /* FORMAT_PARAMETER */
+             
+           default:
+             return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+           }
+         ch = data->current;
+         index = parameters[i].indexAfterSpecifier;
+         i++;
+       }
+      else /* Not an % identifier */
+       {
+         if (isspace((int)format[index]))
+           {
+             /* Whitespaces may match any amount of whitespaces */
+             ch = TrioSkipWhitespaces(data);
+           }
+         else if (ch == format[index])
+           {
+             data->InStream(data, &ch);
+           }
+         else
+           return TRIO_ERROR_RETURN(TRIO_EINVAL, index);
+         
+         index++;
+       }
+    }
+  return assignment;
+}
+
+/*************************************************************************
+ * TrioInStreamFile [private]
+ */
+static void
+TrioInStreamFile(trio_T *self,
+                int *intPointer)
+{
+  FILE *file = (FILE *)self->location;
+
+  assert(VALID(self));
+  assert(VALID(file));
+
+  self->current = fgetc(file);
+  self->processed++;
+  self->committed++;
+  
+  if (VALID(intPointer))
+    {
+      *intPointer = self->current;
+    }
+}
+
+/*************************************************************************
+ * TrioInStreamFileDescriptor [private]
+ */
+static void
+TrioInStreamFileDescriptor(trio_T *self,
+                          int *intPointer)
+{
+  int fd = *((int *)self->location);
+  int size;
+  unsigned char input;
+
+  assert(VALID(self));
+
+  size = read(fd, &input, sizeof(char));
+  self->current = (size == 0) ? EOF : input;
+  self->processed++;
+  self->committed++;
+  
+  if (VALID(intPointer))
+    {
+      *intPointer = self->current;
+    }
+}
+
+/*************************************************************************
+ * TrioInStreamString [private]
+ */
+static void
+TrioInStreamString(trio_T *self,
+                  int *intPointer)
+{
+  unsigned char **buffer;
+
+  assert(VALID(self));
+  assert(VALID(self->InStream));
+  assert(VALID(self->location));
+
+  buffer = (unsigned char **)self->location;
+  self->current = (*buffer)[0];
+  if (self->current == NIL)
+    self->current = EOF;
+  (*buffer)++;
+  self->processed++;
+  self->committed++;
+  
+  if (VALID(intPointer))
+    {
+      *intPointer = self->current;
+    }
+}
+
+/*************************************************************************
+ * scanf
+ */
+int
+trio_scanf(const char *format,
+          ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioScan(stdin, 0, TrioInStreamFile, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vscanf(const char *format,
+           va_list args)
+{
+  assert(VALID(format));
+  
+  return TrioScan(stdin, 0, TrioInStreamFile, format, args, NULL);
+}
+
+int
+trio_scanfv(const char *format,
+           void **args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(format));
+  
+  return TrioScan(stdin, 0, TrioInStreamFile, format, dummy, args);
+}
+
+/*************************************************************************
+ * fscanf
+ */
+int
+trio_fscanf(FILE *file,
+           const char *format,
+           ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(file));
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioScan(file, 0, TrioInStreamFile, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vfscanf(FILE *file,
+            const char *format,
+            va_list args)
+{
+  assert(VALID(file));
+  assert(VALID(format));
+  
+  return TrioScan(file, 0, TrioInStreamFile, format, args, NULL);
+}
+
+int
+trio_fscanfv(FILE *file,
+            const char *format,
+            void **args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(file));
+  assert(VALID(format));
+  
+  return TrioScan(file, 0, TrioInStreamFile, format, dummy, args);
+}
+
+/*************************************************************************
+ * dscanf
+ */
+int
+trio_dscanf(int fd,
+           const char *format,
+           ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioScan(&fd, 0, TrioInStreamFileDescriptor, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vdscanf(int fd,
+            const char *format,
+            va_list args)
+{
+  assert(VALID(format));
+  
+  return TrioScan(&fd, 0, TrioInStreamFileDescriptor, format, args, NULL);
+}
+
+int
+trio_dscanfv(int fd,
+             const char *format,
+             void **args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(format));
+  
+  return TrioScan(&fd, 0, TrioInStreamFileDescriptor, format, dummy, args);
+}
+
+/*************************************************************************
+ * sscanf
+ */
+int
+trio_sscanf(const char *buffer,
+           const char *format,
+           ...)
+{
+  int status;
+  va_list args;
+
+  assert(VALID(buffer));
+  assert(VALID(format));
+  
+  va_start(args, format);
+  status = TrioScan(&buffer, 0, TrioInStreamString, format, args, NULL);
+  va_end(args);
+  return status;
+}
+
+int
+trio_vsscanf(const char *buffer,
+            const char *format,
+            va_list args)
+{
+  assert(VALID(buffer));
+  assert(VALID(format));
+  
+  return TrioScan(&buffer, 0, TrioInStreamString, format, args, NULL);
+}
+
+int
+trio_sscanfv(const char *buffer,
+            const char *format,
+            void **args)
+{
+  va_list dummy;
+  shutup_unitialized(&dummy);
+  
+  assert(VALID(buffer));
+  assert(VALID(format));
+  
+  return TrioScan(&buffer, 0, TrioInStreamString, format, dummy, args);
+}
+
diff --git a/org.glite.lb.common/src/triop.h b/org.glite.lb.common/src/triop.h
new file mode 100644 (file)
index 0000000..ca49fab
--- /dev/null
@@ -0,0 +1,138 @@
+/*************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2000 Bjorn Reese and Daniel Stenberg.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
+ * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
+ *
+ ************************************************************************
+ *
+ * Private functions, types, etc. used for callback functions.
+ *
+ * The ref pointer is an opaque type and should remain as such.
+ * Private data must only be accessible through the getter and
+ * setter functions.
+ *
+ ************************************************************************/
+
+#ifndef TRIO_TRIOP_H
+#define TRIO_TRIOP_H
+
+#include <stdlib.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef TRIO_C99
+# define TRIO_C99 1
+#endif
+#ifndef TRIO_BSD
+# define TRIO_BSD 1
+#endif
+#ifndef TRIO_GNU
+# define TRIO_GNU 1
+#endif
+#ifndef TRIO_MISC
+# define TRIO_MISC 1
+#endif
+#ifndef TRIO_UNIX98
+# define TRIO_UNIX98 1
+#endif
+#ifndef TRIO_MICROSOFT
+# define TRIO_MICROSOFT 1
+#endif
+#ifndef TRIO_EXTENSION
+# define TRIO_EXTENSION 1
+#endif
+#ifndef TRIO_WIDECHAR
+# define TRIO_WIDECHAR 0
+#endif
+#ifndef TRIO_ERRORS
+# define TRIO_ERRORS 1
+#endif
+
+#ifndef TRIO_MALLOC
+# define TRIO_MALLOC(n) malloc(n)
+#endif
+#ifndef TRIO_REALLOC
+# define TRIO_REALLOC(x,n) realloc((x),(n))
+#endif
+#ifndef TRIO_FREE
+# define TRIO_FREE(x) free(x)
+#endif
+
+typedef int (*trio_callback_t)(void *ref);
+
+void *trio_register(trio_callback_t callback, const char *name);
+void trio_unregister(void *handle);
+
+const char *trio_get_format(void *ref);
+void *trio_get_argument(void *ref);
+
+/* Modifiers */
+int  trio_get_width(void *ref);
+void trio_set_width(void *ref, int width);
+int  trio_get_precision(void *ref);
+void trio_set_precision(void *ref, int precision);
+int  trio_get_base(void *ref);
+void trio_set_base(void *ref, int base);
+int  trio_get_padding(void *ref);
+void trio_set_padding(void *ref, int is_padding);
+int  trio_get_short(void *ref); /* h */
+void trio_set_shortshort(void *ref, int is_shortshort);
+int  trio_get_shortshort(void *ref); /* hh */
+void trio_set_short(void *ref, int is_short);
+int  trio_get_long(void *ref); /* l */
+void trio_set_long(void *ref, int is_long);
+int  trio_get_longlong(void *ref); /* ll */
+void trio_set_longlong(void *ref, int is_longlong);
+int  trio_get_longdouble(void *ref); /* L */
+void trio_set_longdouble(void *ref, int is_longdouble);
+int  trio_get_alternative(void *ref); /* # */
+void trio_set_alternative(void *ref, int is_alternative);
+int  trio_get_alignment(void *ref); /* - */
+void trio_set_alignment(void *ref, int is_leftaligned);
+int  trio_get_spacing(void *ref); /* (space) */
+void trio_set_spacing(void *ref, int is_space);
+int  trio_get_sign(void *ref); /* + */
+void trio_set_sign(void *ref, int is_showsign);
+int  trio_get_quote(void *ref); /* ' */
+void trio_set_quote(void *ref, int is_quote);
+int  trio_get_upper(void *ref);
+void trio_set_upper(void *ref, int is_upper);
+#if TRIO_C99
+int  trio_get_largest(void *ref); /* j */
+void trio_set_largest(void *ref, int is_largest);
+int  trio_get_ptrdiff(void *ref); /* t */
+void trio_set_ptrdiff(void *ref, int is_ptrdiff);
+int  trio_get_size(void *ref); /* z / Z */
+void trio_set_size(void *ref, int is_size);
+#endif
+
+/* Printing */
+int trio_print_ref(void *ref, const char *format, ...);
+int trio_vprint_ref(void *ref, const char *format, va_list args);
+int trio_printv_ref(void *ref, const char *format, void **args);
+
+void trio_print_int(void *ref, int number);
+void trio_print_uint(void *ref, unsigned int number);
+/*  void trio_print_long(void *ref, long number); */
+/*  void trio_print_ulong(void *ref, unsigned long number); */
+void trio_print_double(void *ref, double number);
+void trio_print_string(void *ref, char *string);
+void trio_print_pointer(void *ref, void *pointer);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* TRIO_TRIOP_H */
diff --git a/org.glite.lb.common/src/ulm_parse.c b/org.glite.lb.common/src/ulm_parse.c
new file mode 100644 (file)
index 0000000..1a40d57
--- /dev/null
@@ -0,0 +1,410 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+/*========= DATA =====================================================*/
+
+#include "ulm_parse.h"
+
+/*========= FUNCTIONS ================================================*/
+
+/* -- Internal function prototype -- */
+void edg_wll_ULMSplitDate( const char *s,
+                      unsigned int *year,
+                      unsigned int *mon,
+                      unsigned int *day,
+                      unsigned int *hour,
+                      unsigned int *min,
+                      unsigned int *sec,
+                      unsigned long *usec );
+int edg_wll_ULMisalphaext( int c);
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_ULMNewParseTable -- Allocate memory for new parse table
+ *
+ * Calls: malloc, strdup
+ *
+ * Algorithm:
+ *
+ *----------------------------------------------------------------------
+ */
+p_edg_wll_ULMFields edg_wll_ULMNewParseTable(LogLine logline)
+{
+  p_edg_wll_ULMFields this = (p_edg_wll_ULMFields) calloc(1,sizeof(edg_wll_ULMFields));
+  LogLine ln = logline;
+
+  /* Strip leading spaces */
+  for ( ; *ln && isblank(*ln); ln++ );
+
+  this->names = NULL;
+  this->vals = NULL;
+  this->num = 0;
+  this->raw = strdup(ln);
+
+  return this;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_ULMFreeParseTable -- Free memory allocated for parse table
+ *
+ * Calls: free
+ *
+ * Algorithm:
+ *
+ *----------------------------------------------------------------------
+ */
+void edg_wll_ULMFreeParseTable(p_edg_wll_ULMFields this)
+{   
+   EDG_WLL_ULM_CLEAR_FIELDS(this);
+   if (this->raw) free(this->raw);
+   if (this) free(this);
+}
+
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_ULMProcessParseTable -- Break UML message into fields.
+ *
+ * Calls: strchr, strrchr
+ *
+ * Algorithm: 
+ *     LogLine is of the ULM form as described in draft-abela-ulm-05.
+ *     - the ULM_EQ symbol separates name,value pairs 
+ *     - quote marks must be taken into account 
+ *     1. count ULM_EQ and ULM_SP symbols,
+ *     2. allocate the integer arrays 'names' and 'vals' which hold 
+ *        the indices of subsequent name and value strings,
+ *     3. the original raw data will be peppered with \0's so that raw[index] 
+ *        will give the proper string if index is in names[] or vals[]
+ * 
+ *      The actual algorithm to get these indices is a simple array lookup.
+ *
+ *     The following illegal formats are caught:
+ *             1. no name=value pairs
+ *             2. space or tab next to delimiter
+ *             3. logline starts or ends with delimiter
+ *             4. no spaces between successive delimiters
+ *             5. illegal character after ending ULM_QM
+ *
+ *----------------------------------------------------------------------
+ */
+int edg_wll_ULMProcessParseTable(p_edg_wll_ULMFields this)
+{
+  char *func = "edg_wll_ULMProcessParseTable";
+  char *eq;
+  int  i,j;
+  int  eqCnt,qmCnt,spCnt;
+  int  iArrayEQ[ULM_FIELDS_MAX];
+  int  iArraySP[ULM_FIELDS_MAX];
+  size_t size;
+
+  if ( (this == NULL) || (this->raw == NULL) || (*this->raw == '\0')) {
+    fprintf(stderr,"%s: PARSE ERROR: Nothing to parse.\n",func);
+    return ULM_PARSE_ERROR;                                     
+  }
+
+  /* Init data */
+  EDG_WLL_ULM_CLEAR_FIELDS(this);
+
+  for (i=0; i<ULM_FIELDS_MAX; i++) { 
+    iArrayEQ[i] = 0;
+    iArraySP[i] = 0;
+  }
+
+  i = j = 0;
+  qmCnt = eqCnt = spCnt = 0;
+
+  size = strlen(this->raw);
+
+  /* Count number of ULM_EQ and ULM_SP 
+   * and replace all ULM_LF by nul characters */
+  for (i=0; i< (int)size; i++) {
+    switch (this->raw[i]) {
+      case ULM_SP :
+      case ULM_TB :
+         if (qmCnt == 0) { iArraySP[spCnt] = i; spCnt++; }
+         break;
+      case ULM_EQ :
+        if (i==0) {
+               fprintf(stderr,"%s: PARSE ERROR: '%c' at the beginning of log line.\n", func, ULM_EQ);
+               return ULM_PARSE_ERROR;
+        }
+         if (qmCnt == 0) { 
+           if (isblank(this->raw[i-1]) || (!edg_wll_ULMisalphaext(this->raw[i-1]))) {
+             fprintf(stderr,"%s: PARSE ERROR: Disallowed character ('%c') or space before delimiter '%c'.\n",
+                     func,this->raw[i-1],ULM_EQ);
+             return ULM_PARSE_ERROR;
+           }
+           if (isblank(this->raw[i+1]) || ((!edg_wll_ULMisalphaext(this->raw[i-1])) && (this->raw[i+1] != ULM_QM ))) {
+             fprintf(stderr,"%s: PARSE ERROR: Disallowed character ('%c') or space after delimiter '%c'.\n",
+                     func,this->raw[i+1],ULM_EQ);
+             return ULM_PARSE_ERROR;
+           }
+           iArrayEQ[eqCnt] = i; 
+           eqCnt++; 
+         }
+         break;
+      case ULM_LF :
+         if (qmCnt == 0) { this->raw[i] = '\0'; }
+        break;
+      case ULM_QM :
+         if (this->raw[i-1] != ULM_BS) {
+           if (qmCnt == 0) qmCnt++;
+           else qmCnt--;
+         }              
+         if ((qmCnt == 0) && (!isspace(this->raw[i+1]) && (this->raw[i+1] != '\0'))) {
+             fprintf(stderr,"%s: PARSE ERROR: Disallowed character ('%c') after ending '%c'at i=%d size=%d char=%d.\n",
+                     func,this->raw[i+1],ULM_QM,i,size,this->raw[i+1]);
+             for (j=0; j<=i; j++) fputc(this->raw[j],stderr);
+             fputc(ULM_LF,stderr);
+             return ULM_PARSE_ERROR;
+         }
+         break;
+      default :
+         break;
+    } /* switch */
+  } /* for */
+
+  if (eqCnt == 0) {
+    fprintf(stderr,"%s: PARSE ERROR: No '%c' in line \"%s\"\n",func,ULM_EQ,this->raw);
+    return ULM_PARSE_ERROR;                    
+  }
+
+  if (this->raw[0] == ULM_EQ) {
+    fprintf(stderr,"%s: PARSE ERROR: Need at least 1 letter for the first field name.\n",func);
+    return ULM_PARSE_ERROR;                    
+  }
+
+  if (qmCnt != 0) {
+    fprintf(stderr,"%s: PARSE ERROR: Last quoted value did not finish.\n",func);
+    return ULM_PARSE_ERROR;                    
+  }
+
+  /* Allocate names, vals arrays */
+  this->names = (int *) malloc(eqCnt*sizeof(int));
+  this->vals =  (int *) malloc(eqCnt*sizeof(int));
+
+  this->names[0] = (int)(0);
+  this->vals[0]  = (int)(iArrayEQ[0] + 1);
+
+  /* 
+   * Main loop 
+   */
+  for (i=1; i<eqCnt; i++) {
+    eq = &this->raw[iArrayEQ[i]]; 
+    j = 1;
+    while (edg_wll_ULMisalphaext(*(eq-j))) {
+      j++;
+    }
+    if (isblank(*(eq-j))) {
+       this->names[i] = (int)(iArrayEQ[i] - j + 1);
+       this->vals[i]  = (int)(iArrayEQ[i] + 1);
+    }
+    else {
+       fprintf(stderr,"%s: PARSE ERROR: Disallowed character '%c' for field name \
+(e.g. no space between successive delimiters).\n",func,*(eq-j));
+       return ULM_PARSE_ERROR; 
+    }
+  } /* for */
+
+  /* Replace delimiters and intervening whitespace by nul characters */
+  for (i=0; i<eqCnt; i++) this->raw[iArrayEQ[i]] = '\0';   
+  for (i=0; i<spCnt; i++) this->raw[iArraySP[i]] = '\0';
+
+  this->num = eqCnt;
+
+  /* Debug dump of parsed fields */
+//  for( i=0; i<eqCnt; i++ ) fprintf(stdout,"field[%d]: %s=%s\n",i,this->raw+this->names[i],this->raw+this->vals[i]);
+
+  return ULM_PARSE_OK;
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * edg_wll_ULMisalphaext - test if the character is an ALPHAEXT as described in 
+ *              draft-abela-ulm-05
+ *---------------------------------------------------------------------------
+ */
+int edg_wll_ULMisalphaext( int c) {
+
+  return (isalnum(c) || (c == '.') || (c == '-') || c == '_');
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_ULMGetNameAt -- Get name at index.
+ *
+ * Calls: 
+ *
+ * Algorithm: array lookup
+ *
+ *----------------------------------------------------------------------
+ */
+char *edg_wll_ULMGetNameAt( p_edg_wll_ULMFields this, int index )
+{
+  if ( index < 0 || index > this->num )
+    return NULL;
+  return (char *)&(this->raw[this->names[index]]);
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
+ * edg_wll_ULMGetValueAt -- Get value at index
+ *
+ * Calls: 
+ *
+ * Algorithm: array lookup
+ *
+ *----------------------------------------------------------------------
+ */
+char *edg_wll_ULMGetValueAt( p_edg_wll_ULMFields this, int index ) 
+{
+  if ( index < 0 || index > this->num )
+    return NULL;
+  return (char *)&(this->raw[this->vals[index]]);
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * edg_wll_ULMDateToDouble -- Calculate date in sec. since 1/1/1970 from string.
+ *                Algorithm borrowed from linux kernel source code,
+ *                i.e. Linus Torvalds, who in turn credits it to Gauss.
+ *
+ * PRE: Input is properly formatted, non-null, need _not_ be null-terminated.
+ * IN : String in format YYYYMMDDHHmmss.uuuuuu
+ *        YYYY  = 4 digit year
+ *        MM    = 2 digit month (1..12)
+ *        DD    = 2 digit day of month (1..31)
+ *        HH    = 2 digit hour of day ( 0..23 )
+ *        mm    = 2 digit minute of hour ( 0..59 )
+ *        ss    = 2 digit second of minute ( 0..59 )
+ *        uuuuuu= 1..6 digit microsecond of second ( 0..999999 )
+ * OUT: Number of seconds, and fraction accurate to at most microseconds,
+ *      elapsed since 1/1/1970 (GMT).
+ *
+ * edg_wll_ULMDateToTimeval -- the same, except it returns timeval
+ *---------------------------------------------------------------------------
+ */
+double edg_wll_ULMDateToDouble( const char *s )
+{
+  unsigned int year, mon, day, hour, min, sec=0;
+  unsigned long usec=0L;
+
+  edg_wll_ULMSplitDate( s, &year, &mon, &day, &hour, &min, &sec, &usec );
+
+  if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */
+    mon += 12;      /* Puts Feb last since it has leap day */
+    year -= 1;
+  }
+  return (double)
+    ( ( ( (
+           (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+           year*365 - 719499
+           )*24 + hour /* now have hours */
+          )*60 + min /* now have minutes */
+        )*60 + sec /* seconds */
+      ) + (double)( usec / 1E6 ); /* microseconds */
+}
+
+void edg_wll_ULMDateToTimeval( const char *s, struct timeval *tv )
+{
+  unsigned int year, mon, day, hour, min, sec=0;
+  unsigned long usec=0L;
+
+  edg_wll_ULMSplitDate( s, &year, &mon, &day, &hour, &min, &sec, &usec );
+
+  if (0 >= (int) (mon -= 2)) {    /* 1..12 -> 11,12,1..10 */
+    mon += 12;      /* Puts Feb last since it has leap day */
+    year -= 1;
+  }
+  tv->tv_sec = (long)
+    ( ( ( (
+           (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+           year*365 - 719499
+           )*24 + hour /* now have hours */
+          )*60 + min /* now have minutes */
+        )*60 + sec /* seconds */
+      );
+  tv->tv_usec = usec; /* microseconds */
+}
+
+/*
+ *---------------------------------------------------------------------------
+ * edg_wll_ULMSplitDate -- Efficiently break date into parsed component parts.
+ *---------------------------------------------------------------------------
+ */
+#define DIG(x) ((int)((x)-'0'))
+void edg_wll_ULMSplitDate( const char *s,
+                      unsigned int *year,
+                      unsigned int *mon,
+                      unsigned int *day,
+                      unsigned int *hour,
+                      unsigned int *min,
+                      unsigned int *sec,
+                      unsigned long *usec )
+{
+   *year = DIG(s[0]) * 1000 + DIG(s[1]) * 100 + DIG(s[2]) * 10 + DIG(s[3]);
+   *mon  = DIG(s[4]) * 10 + DIG(s[5]);
+   *day  = DIG(s[6]) * 10 + DIG(s[7]);
+   *hour = DIG(s[8]) * 10 + DIG(s[9]);
+   *min  = DIG(s[10]) * 10 + DIG(s[11]);
+   *sec  = DIG(s[12]) * 10 + DIG(s[13]);
+   if ( s[14] == '.' ) *usec = atol(s+15);
+}
+#undef DIG
+
+/*
+ *---------------------------------------------------------------------------
+ * edg_wll_ULMTimevalToDate -- Take a seconds, microseconds argument and convert it
+ *                   to a date string that edg_wll_ULMDateToDouble could parse.
+ *
+ * CALL: gmtime, strftime
+ *
+ * PRE : seconds, usec >= 0 , usec < 1000000 (checked)
+ *       date string has DATE_STRING_LENGTH+1 bytes allocated (not checked)
+ * IN  : seconds, useconds
+ * OUT : date string 'dstr'.
+ * RTRN: 0=OK, other=FAILURE
+ * POST: This is the inverse of edg_wll_ULMDateToDouble, i.e.
+ *         edg_wll_ULMDateToDouble( edg_wll_ULMTimevalToDate( sec, usec ) ) = sec.usec
+ *---------------------------------------------------------------------------
+ */
+int edg_wll_ULMTimevalToDate( long sec, long usec, char *dstr )
+{
+  char *func = "edg_wll_ULMTimevalToDate";
+  struct tm *tp;
+  int        len;
+
+  if ( sec < 0 || usec < 0 || usec > 999999 )
+    return 1;
+
+  tp = gmtime( (const time_t *) &sec );
+  if ( tp == NULL )
+    return 1;
+
+  len = strftime( dstr,
+                  ULM_DATE_STRING_LENGTH+1-7,
+                  "%Y%m%d%H%M%S",
+                  tp );
+  if ( len != ULM_DATE_STRING_LENGTH-7 ) {
+    fprintf(stderr,"%s: bad strftime() return value: %d\n",func, len);
+    return 1;
+  }
+
+  sprintf( dstr + ULM_DATE_STRING_LENGTH-7, ".%06ld", usec );
+
+  return 0;
+}
+
diff --git a/org.glite.lb.common/src/xml_conversions.c b/org.glite.lb.common/src/xml_conversions.c
new file mode 100644 (file)
index 0000000..89ad510
--- /dev/null
@@ -0,0 +1,835 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <expat.h>
+
+#include "glite/lb/trio.h"
+
+#include "xml_conversions.h"
+
+
+
+static const struct timeval null_timeval = {0,0};
+
+
+/************************************************************************/
+/* Context manipulation functions                                      */      
+
+
+void edg_wll_initXMLCtx(edg_wll_XML_ctx *c) {
+       c->ctx = NULL;
+        c->p = NULL;
+       c->message_body = NULL;
+        c->eventCode = EDG_WLL_EVENT_UNDEF;
+        c->position = 0;
+        c->position2 = 0;
+       c->max_index = -1;
+       c->row = 0;
+       c->row2 = 0;
+        c->level = 0;
+        memset(&(c->element), 0, sizeof(c->element));
+        c->char_buf = NULL;
+        c->char_buf_len = 0;
+        c->XML_tag = NULL;
+        c->XML_tag2 = NULL;
+       c->job_conditions = NULL;
+       c->event_conditions = NULL;
+       c->type = EDG_WLL_QUERY_TYPE_UNDEF;
+       c->flags = 0;
+        c->jobsOutGlobal = NULL;
+        c->eventsOutGlobal = NULL;
+        c->jobStatGlobal = NULL;
+       memset(&(c->jobStatSingleGlobal),0,sizeof(c->jobStatSingleGlobal));
+       c->strListGlobal = NULL;
+       c->intListGlobal = NULL;
+       c->indexToTag    = NULL;
+       c->tagToIndex    = NULL;
+       c->tagListGlobal = NULL;
+       c->stsListGlobal = NULL;
+       memset(&(c->purgeRequestGlobal),0,sizeof(c->purgeRequestGlobal));
+       memset(&(c->purgeResultGlobal),0,sizeof(c->purgeResultGlobal));
+       memset(&(c->dumpRequestGlobal),0,sizeof(c->dumpRequestGlobal));
+       memset(&(c->dumpResultGlobal),0,sizeof(c->dumpResultGlobal));
+       memset(&(c->loadRequestGlobal),0,sizeof(c->loadRequestGlobal));
+       memset(&(c->loadResultGlobal),0,sizeof(c->loadResultGlobal));
+       c->notifFunction = NULL;
+       c->notifClientAddress = NULL;
+       c->notifId = NULL;
+       c->notifChangeOp = EDG_WLL_NOTIF_NOOP;
+       c->notifValidity = -1;
+       c->attrsGlobal  = NULL;
+       c->errCode = 0;
+       c->bound = 0;
+       c->errDesc = NULL;
+       c->stat_begin = 0;
+       c->jobQueryRec_begin = 0;
+        c->errtxt = NULL;
+       c->warntxt = NULL;
+}
+
+
+void edg_wll_freeXMLCtx(edg_wll_XML_ctx *c) {
+        if (c->char_buf) free(c->char_buf);
+        if (c->errtxt) free(c->errtxt);
+        if (c->warntxt) free(c->warntxt);
+       if (c->XML_tag) free(c->XML_tag);
+       if (c->XML_tag2) free(c->XML_tag2);
+}
+
+
+void edg_wll_freeBuf(edg_wll_XML_ctx *c) {
+        free(c->char_buf);
+        c->char_buf = NULL;
+        c->char_buf_len = 0;
+}
+
+
+
+/************************************************************************/
+/* type to XML conversion functions                                    */
+
+
+/* edg_wll_add_string_to_XMLBody(&body, eventsOut[i].jobMatch.destination, "destination", NULL) */
+
+void edg_wll_add_string_to_XMLBody(char **body, const char *toAdd, const char *tag, const char *null)
+{
+       if ( toAdd != null ) {
+               char *newBody;
+               
+               trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xs</%s>\r\n", *body, tag, toAdd, tag);
+
+               free(*body);
+               *body = newBody;
+       }
+}
+
+void edg_wll_add_tagged_string_to_XMLBody(char **body, const char *toAdd, const char *tag, 
+                                         const char *name, const char *tag2, const char *null)
+{
+       if ( toAdd != null ) {
+               char *newBody;
+               
+               trio_asprintf(&newBody,"%s\t\t\t<%s %s=\"%|Xs\">%|Xs</%s>\r\n", 
+                       *body, tag, tag2, name, toAdd, tag);
+
+               free(*body);
+               *body = newBody;
+       }
+}
+
+/* edg_wll_add_int_to_XMLBody(&body, eventsOut[i].jobMatch.code, "code", 0) */
+
+void edg_wll_add_int_to_XMLBody(char **body, const int toAdd, const char *tag, const int null)
+{
+       if (toAdd != null) {
+                char *newBody;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xd</%s>\r\n", *body, tag, toAdd, tag);
+
+                free(*body);
+                *body = newBody;
+       }
+}
+
+
+/* edg_wll_add_timeval_to_XMLBody(&body, eventsOut[i].any.tv, "timestamp", -1) */
+
+void edg_wll_add_timeval_to_XMLBody(char **body, struct timeval toAdd, const char *tag, const struct timeval null)
+{
+
+       if (toAdd.tv_sec != null.tv_sec || toAdd.tv_usec != null.tv_usec) {
+                char *newBody;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%ld.%06ld</%s>\r\n", 
+               *body, tag, toAdd.tv_sec, toAdd.tv_usec, tag);
+
+                free(*body);
+                *body = newBody;
+       }
+}
+
+
+/* edg_wll_add_jobid_to_XMLBody(&body, eventsOut[i].any.jobId, "jobId", NULL) */
+
+void edg_wll_add_jobid_to_XMLBody(char **body, edg_wlc_JobId toAdd, const char *tag, const void *null)
+{
+       if (toAdd != (edg_wlc_JobId) null) {
+                char *newBody, *pom;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xs</%s>\r\n",
+                *body, tag, pom = edg_wlc_JobIdUnparse(toAdd), tag);
+
+                free(*body);
+               free(pom);
+                *body = newBody;
+       }
+}
+
+
+void edg_wll_add_notifid_to_XMLBody(char **body, edg_wll_NotifId toAdd, const char *tag, const void *null)
+{
+       if (toAdd != (edg_wll_NotifId) null) {
+                char *newBody, *pom;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xs</%s>\r\n",
+                *body, tag, pom = edg_wll_NotifIdUnparse(toAdd), tag);
+
+                free(*body);
+               free(pom);
+                *body = newBody;
+       }
+}
+
+
+/* edg_wll_add_edg_wll_JobStatCode_to_XMLBody(&body, eventsOut[i].any.jobId, "jobId", EDG_WLL_JOB_UNDEF) */
+
+void edg_wll_add_edg_wll_JobStatCode_to_XMLBody(char **body, edg_wll_JobStatCode toAdd, const char *tag, const edg_wll_JobStatCode null)
+{
+       if (toAdd != null) {
+                char *newBody, *pom;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xs</%s>\r\n",
+                       *body, tag, pom = edg_wll_StatToString(toAdd), tag);
+
+                free(*body);
+               free(pom);
+                *body = newBody;
+       }
+}
+
+void edg_wll_add_edg_wll_EventCode_to_XMLBody(char **body, edg_wll_EventCode toAdd, const char *tag, const edg_wll_EventCode null)
+{
+       char *newBody, *pom;
+       
+       if (toAdd != null) {
+               trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xs</%s>\r\n",
+                       *body, tag, pom = edg_wll_EventToString(toAdd), tag);   
+
+               free(*body);
+               free(pom);
+               *body = newBody;
+       }
+}
+
+
+void edg_wll_add_time_t_to_XMLBody(char **body, const time_t toAdd, const char *tag, const time_t null)
+{
+       if (toAdd != null) {
+                char *newBody;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xld</%s>\r\n", *body, tag, toAdd, tag);
+
+                free(*body);
+                *body = newBody;
+       }
+}
+
+
+void edg_wll_add_tagged_time_t_to_XMLBody(char **body, const time_t toAdd, const char *tag,
+                                          const char *name, const char *tag2, const time_t null)
+{
+        if ( toAdd != null ) {
+                char *newBody;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s %s=\"%|Xs\">%|Xld</%s>\r\n",
+                        *body, tag, tag2, name, toAdd, tag);
+
+                free(*body);
+                *body = newBody;
+        }
+}
+
+
+void edg_wll_add_uint16_t_to_XMLBody(char **body, const uint16_t toAdd, const char *tag, const uint16_t null)
+{
+       if (toAdd != null) {
+                char *newBody;
+
+                trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xu</%s>\r\n", *body, tag, toAdd, tag);
+
+                free(*body);
+                *body = newBody;
+       }
+}
+
+
+void edg_wll_add_logsrc_to_XMLBody(char **body, const edg_wll_Source toAdd, const char *tag, const edg_wll_Source null)
+{
+        if (toAdd != null) {
+               char *newBody, *pom;
+
+               trio_asprintf(&newBody,"%s\t\t\t<%s>%|Xs</%s>\r\n",
+                        *body, tag, pom = edg_wll_SourceToString(toAdd), tag);
+               
+               free(*body);
+               free(pom);
+               *body = newBody;
+       }
+}
+
+void edg_wll_add_strlist_to_XMLBody(char **body, char * const *toAdd, const char *tag,  const char *subTag, const char *indent,  const  char *null)
+{
+        char *pomA, *pomB, *newBody;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0;
+        int *len_list = NULL;
+
+       
+       if (!toAdd) return;
+               
+        while (toAdd[i] != null) {
+                len = trio_asprintf(&pomA,"%s\t<%s>%|Xs</%s>\r\n",
+                        indent,subTag,toAdd[i],subTag);
+
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) + 1);
+       pomB = pomA;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+       *pomB = '\0';
+        free(list);
+        free(len_list);
+
+       asprintf(&newBody,"%s%s<%s>\r\n%s%s</%s>\r\n", *body, indent, tag, pomA, indent, tag);
+       free(*body);
+       free(pomA);
+       *body = newBody;        
+}
+
+void edg_wll_add_intlist_to_XMLBody(char **body, const int *toAdd, const char *tag, char *(*indexToTag)(), const char *indent, const int from, const int to)
+{
+        char *pomA, *pomB, *newBody;
+        char **list = NULL;
+        int i, len, tot_len = 0;
+        int *len_list = NULL;
+
+       
+       i = from;
+        while (i <= to) {
+                len = trio_asprintf(&pomA,"%s\t<%s>%|Xd</%s>\r\n",
+                        indent, indexToTag(i-1),toAdd[i],indexToTag(i-1));
+
+               i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) + 1);
+       pomB = pomA;
+
+        i = from;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+       *pomB = '\0';
+        free(list);
+        free(len_list);
+
+       asprintf(&newBody,"%s%s<%s>\r\n%s%s</%s>\r\n", *body, indent, tag, pomA, indent, tag);
+        free(*body);
+        free(pomA);
+        *body = newBody;
+}
+
+
+void edg_wll_add_taglist_to_XMLBody(char **body,  const edg_wll_TagValue *toAdd, const char *tag,  const char *subTag, const char *subTag2, const char *indent, const  char *null)
+{
+        char *pomA, *pomB, *newBody;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0;
+        int *len_list = NULL;
+
+
+        while (toAdd && (toAdd[i].tag != null) ) {
+                len = trio_asprintf(&pomA,"%s\t<%s %s=\"%|Xs\">%|Xs</%s>\r\n",
+                        indent,subTag,subTag2,toAdd[i].tag,toAdd[i].value,subTag);
+
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) + 1);
+       pomB = pomA;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+       *pomB = '\0';
+        free(list);
+        free(len_list);
+
+       asprintf(&newBody,"%s%s<%s>\r\n%s%s</%s>\r\n", *body, indent, tag, pomA, indent, tag);
+       free(*body);
+       free(pomA);
+       *body = newBody;        
+}
+
+
+void edg_wll_add_time_t_list_to_XMLBody(char **body, const time_t *toAdd, const char *tag, char *(*indexToTag)(), const char *indent, const int from, const int to)
+{
+        char *pomA, *pomB, *newBody;
+        char **list = NULL;
+        int i, len, tot_len = 0;
+        int *len_list = NULL;
+
+       
+       i = from;
+        while (i < to) {
+                len = trio_asprintf(&pomA,"%s\t<%s>%|Xld</%s>\r\n",
+                        indent, indexToTag(i),toAdd[i],indexToTag(i));
+
+               i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) + 1);
+       pomB = pomA;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+       *pomB = '\0';
+        free(list);
+        free(len_list);
+
+       asprintf(&newBody,"%s%s<%s>\r\n%s%s</%s>\r\n", *body, indent, tag, pomA, indent, tag);
+        free(*body);
+        free(pomA);
+        *body = newBody;
+}
+
+
+// void edg_wll_add_stslist_to_XMLBody(char **body, const edg_wll_JobStat *toAdd, const char *tag, const char *UNUSED_subTag, const int null)
+// in lbserver/lb_xml_parse.c.T
+
+/************************************************************************/
+/* string to type conversion functions                                 */
+
+
+/* XMLCtx->eventsOutGlobal[XMLCtx->position].any.prog = edg_wll_from_string_to_string(XMLCtx); */
+char *edg_wll_from_string_to_string(edg_wll_XML_ctx *XMLCtx)
+{
+       return(XMLCtx->char_buf);
+} 
+
+
+/* XMLCtx->eventsOutGlobal[XMLCtx->position].any.jobId = edg_wll_from_string_to_dgJobId(XMLCtx); */
+edg_wlc_JobId edg_wll_from_string_to_jobid(edg_wll_XML_ctx *XMLCtx)
+{
+       edg_wlc_JobId out;
+
+       edg_wlc_JobIdParse(XMLCtx->char_buf, &out);
+       edg_wll_freeBuf(XMLCtx); 
+
+       return(out);
+}
+
+
+
+edg_wll_NotifId edg_wll_from_string_to_notifid(edg_wll_XML_ctx *XMLCtx)
+{
+       edg_wll_NotifId out;
+
+       edg_wll_NotifIdParse(XMLCtx->char_buf, &out);
+       edg_wll_freeBuf(XMLCtx); 
+
+       return(out);
+}
+
+
+
+edg_wll_JobStatCode edg_wll_from_string_to_edg_wll_JobStatCode(edg_wll_XML_ctx *XMLCtx)
+{
+       edg_wll_JobStatCode out;
+       
+       out = edg_wll_StringToStat(XMLCtx->char_buf);
+       edg_wll_freeBuf(XMLCtx); 
+
+       return(out);
+}
+
+
+
+
+/* XMLCtx->eventsOutGlobal[XMLCtx->position].jobClear.clearReason = 
+       edg_wll_from_string_to_int(XMLCtx);                             */
+int edg_wll_from_string_to_int(edg_wll_XML_ctx *XMLCtx)
+{
+        int out;
+
+        out = atoi(XMLCtx->char_buf);
+        edg_wll_freeBuf(XMLCtx);
+
+        return(out);
+}
+
+
+long edg_wll_from_string_to_long(edg_wll_XML_ctx *XMLCtx)
+{
+        long out;
+
+        out = atol(XMLCtx->char_buf);
+        edg_wll_freeBuf(XMLCtx);
+
+        return(out);
+}
+
+
+uint16_t edg_wll_from_string_to_uint16_t(edg_wll_XML_ctx *XMLCtx)
+{
+        return( (uint16_t) edg_wll_from_string_to_int(XMLCtx) );
+}
+
+
+struct timeval edg_wll_from_string_to_timeval(edg_wll_XML_ctx *XMLCtx)
+{
+       struct timeval out;
+       char *needle, *nil;
+
+       out.tv_sec  = strtol(XMLCtx->char_buf, &needle, 10);
+       out.tv_usec = strtol(needle+1, &nil, 10);
+       edg_wll_freeBuf(XMLCtx);
+
+       return(out);
+}
+
+
+time_t edg_wll_from_string_to_time_t(edg_wll_XML_ctx *XMLCtx)
+{
+        return( (time_t) edg_wll_from_string_to_long(XMLCtx) );
+}
+
+
+edg_wll_Source edg_wll_from_string_to_logsrc(edg_wll_XML_ctx *XMLCtx)
+{
+       edg_wll_Source  out = edg_wll_StringToSource(XMLCtx->char_buf);
+       
+       edg_wll_freeBuf(XMLCtx);
+       return(out);
+}
+
+
+
+/************************************************************************/
+/* various conversion functions                                        */
+
+
+char *edg_wll_stat_flags_to_string(int flags)
+{
+        char *cflags = NULL, *temp_cflags = NULL;
+
+
+        if (flags & EDG_WLL_STAT_CLASSADS) asprintf(&cflags,"%s","classadd");
+        if (flags & EDG_WLL_STAT_CHILDREN) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"children");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","children");
+        }
+        if (flags & EDG_WLL_STAT_CHILDSTAT) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"childstat");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","childstat");
+        }
+        if (flags & EDG_WLL_STAT_NO_JOBS) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"no_jobs");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","no_jobs");
+        }
+        if (flags & EDG_WLL_STAT_NO_STATES) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"no_states");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","no_states");
+        }
+
+        if (!cflags) cflags = strdup("");
+
+        return(cflags);
+}
+
+
+int edg_wll_string_to_stat_flags(char *cflags)
+{
+       int flags = 0;
+       char *sflag, *last;
+
+       if (cflags == NULL) return 0;
+
+       sflag = strtok_r(cflags, "+", &last);
+       while (sflag != NULL) {
+               if (!strcmp(sflag,"classadd")) flags = flags | EDG_WLL_STAT_CLASSADS;
+               if (!strcmp(sflag,"children")) flags = flags | EDG_WLL_STAT_CHILDREN;
+               if (!strcmp(sflag,"childstat")) flags = flags | EDG_WLL_STAT_CHILDSTAT;
+               if (!strcmp(sflag,"no_jobs")) flags = flags | EDG_WLL_STAT_NO_JOBS;
+               if (!strcmp(sflag,"no_states")) flags = flags | EDG_WLL_STAT_NO_STATES;
+               sflag = strtok_r(NULL, "+", &last);
+       } 
+
+       return(flags);
+}
+
+
+char *edg_wll_purge_flags_to_string(int flags)
+{
+        char *cflags = NULL, *temp_cflags = NULL;
+
+
+        if (flags & EDG_WLL_PURGE_REALLY_PURGE) asprintf(&cflags,"%s","really_purge");
+        if (flags & EDG_WLL_PURGE_LIST_JOBS) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"list_jobs");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","list_jobs");
+        }
+        if (flags & EDG_WLL_PURGE_SERVER_DUMP) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"server_dump");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","server_dump");
+        }
+        if (flags & EDG_WLL_PURGE_CLIENT_DUMP) {
+                if (cflags) {
+                        asprintf(&temp_cflags,"%s+%s",cflags,"client_dump");
+                        free(cflags);
+                        cflags=temp_cflags;
+                }
+                else asprintf(&cflags,"%s","client_dump");
+        }
+
+        if (!cflags) cflags = strdup("");
+
+        return(cflags);
+}
+
+
+int edg_wll_string_to_purge_flags(char *cflags)
+{
+       int flags = 0;
+       char *sflag, *last;
+
+       if (cflags == NULL) return 0;
+
+       sflag = strtok_r(cflags, "+", &last);
+       while (sflag != NULL) {
+               if (!strcmp(sflag,"really_purge")) flags = flags | EDG_WLL_PURGE_REALLY_PURGE;
+               if (!strcmp(sflag,"list_jobs")) flags = flags | EDG_WLL_PURGE_LIST_JOBS;
+               if (!strcmp(sflag,"server_dump")) flags = flags | EDG_WLL_PURGE_SERVER_DUMP;
+               if (!strcmp(sflag,"client_dump")) flags = flags | EDG_WLL_PURGE_CLIENT_DUMP;
+               sflag = strtok_r(NULL, "+", &last);
+       } 
+
+       return(flags);
+}
+
+
+/* Functions for conversion of DUMP constants */
+
+static const char * const dumpConsts[] = {
+       "EDG_WLL_DUMP_NOW",
+       "EDG_WLL_DUMP_LAST_START",
+       "EDG_WLL_DUMP_LAST_END",
+};
+
+int edg_wll_StringToDumpConst(const char *name)
+
+{
+        int     i;
+
+        for (i=0; i<sizeof(dumpConsts)/sizeof(dumpConsts[0]); i++)
+                if (strcasecmp(dumpConsts[i],name) == 0) return -(i+1);
+        return 1;
+}
+
+char *edg_wll_DumpConstToString(int dumpConst)
+{
+        if (dumpConst >= 0 || -(dumpConst) > sizeof(dumpConsts)/sizeof(dumpConsts[0])) return (char *) NULL;
+        return strdup(dumpConsts[-(dumpConst+1)]);
+}
+
+
+
+/* Functions for conversion of DONE CODE */ 
+
+static const char * const done_codeConsts[] = {
+       "DONE_CODE_OK",
+       "DONE_CODE_FAILED",
+       "DONE_CODE_CANCELLED",
+};
+
+int edg_wll_StringTodone_code(const char *name)
+
+{
+        int     i;
+
+        for (i=0; i<sizeof(done_codeConsts)/sizeof(done_codeConsts[0]); i++)
+                if (strcasecmp(done_codeConsts[i],name) == 0) return i;
+        return 1;
+}
+
+char *edg_wll_done_codeToString(int done_codeConst)
+{
+        if (done_codeConst < 0 || (done_codeConst) > sizeof(done_codeConsts)/sizeof(done_codeConsts[0])) return (char *) NULL;
+        return strdup(done_codeConsts[done_codeConst]);
+}
+
+
+
+/* Functions for conversion of QUERY ATTRIBUTES */ 
+
+static const char * const query_attrConsts[] = {
+        "EDG_WLL_QUERY_ATTR_UNDEF",
+        "EDG_WLL_QUERY_ATTR_JOBID",
+        "EDG_WLL_QUERY_ATTR_OWNER",
+        "EDG_WLL_QUERY_ATTR_STATUS",
+        "EDG_WLL_QUERY_ATTR_LOCATION",
+        "EDG_WLL_QUERY_ATTR_DESTINATION",
+        "EDG_WLL_QUERY_ATTR_DONECODE",
+        "EDG_WLL_QUERY_ATTR_USERTAG",
+        "EDG_WLL_QUERY_ATTR_TIME",
+        "EDG_WLL_QUERY_ATTR_LEVEL",
+        "EDG_WLL_QUERY_ATTR_HOST",
+        "EDG_WLL_QUERY_ATTR_SOURCE",
+        "EDG_WLL_QUERY_ATTR_INSTANCE",
+        "EDG_WLL_QUERY_ATTR_EVENT_TYPE",
+        "EDG_WLL_QUERY_ATTR_CHKPT_TAG",
+        "EDG_WLL_QUERY_ATTR_RESUBMITTED",
+        "EDG_WLL_QUERY_ATTR_PARENT",
+        "EDG_WLL_QUERY_ATTR_EXITCODE",
+        "EDG_WLL_QUERY_ATTR__LAST",
+};
+
+edg_wll_QueryAttr edg_wll_StringToquery_attr(const char *name)
+
+{
+        int     i;
+
+        for (i=0; i<sizeof(query_attrConsts)/sizeof(query_attrConsts[0]); i++)
+                if (strcasecmp(query_attrConsts[i],name) == 0) return (edg_wll_QueryAttr) i;
+        return EDG_WLL_QUERY_ATTR_UNDEF;
+}
+
+char *edg_wll_query_attrToString(edg_wll_QueryAttr query_attrConst)
+{
+        if (query_attrConst < 0 || (query_attrConst) > sizeof(query_attrConsts)/sizeof(query_attrConsts[0])) return (char *) NULL;
+        return strdup(query_attrConsts[(int) query_attrConst]);
+}
+
+
+
+/* Functions for conversion of NOTIFICATION CHANGE OPERATORS */ 
+
+static const char * const notifChangeOpConsts[] = {
+       "EDG_WLL_NOTIF_NOOP"
+       "EDG_WLL_NOTIF_REPLACE",
+       "EDG_WLL_NOTIF_ADD",
+       "EDG_WLL_NOTIF_REMOVE",
+};
+
+
+
+edg_wll_NotifChangeOp edg_wll_StringToNotifChangeOp(const char *name)
+{
+        int     i;
+
+        for (i=0; i<sizeof(notifChangeOpConsts)/sizeof(notifChangeOpConsts[0]); i++)
+                if (strcasecmp(notifChangeOpConsts[i],name) == 0) return (edg_wll_NotifChangeOp) i;
+        return EDG_WLL_NOTIF_NOOP;
+}
+
+
+
+char *edg_wll_NotifChangeOpToString(edg_wll_NotifChangeOp notifChangeOpConst)
+{
+        if (notifChangeOpConst < 0 || (notifChangeOpConst) > sizeof(notifChangeOpConsts)/sizeof(notifChangeOpConsts[0])) return (char *) NULL;
+        return strdup(notifChangeOpConsts[(int) notifChangeOpConst]);
+}
diff --git a/org.glite.lb.common/src/xml_parse.c.T b/org.glite.lb.common/src/xml_parse.c.T
new file mode 100644 (file)
index 0000000..2cf477c
--- /dev/null
@@ -0,0 +1,2613 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <assert.h>
+
+#include <expat.h>     // Expat header file
+
+#include "globus_config.h"
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+
+#include "glite/lb/trio.h"
+#include "glite/lb/producer.h"
+#include "glite/wms/jobid/cjobid.h"
+
+#include "escape.h"
+#include "context-int.h"
+#include "xml_parse.h"
+#include "xml_conversions.h"    
+
+
+#ifdef __GNUC__ 
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR 
+#endif
+
+#define QUERY_EVENTS_REQUEST_BEGIN     "<edg_wll_QueryEventsRequest"
+#define QUERY_EVENTS_REQUEST_END       "\t</and>\r\n</edg_wll_QueryEventsRequest>"
+#define QUERY_EVENTS_OREC_BEGIN                "\t\t<orEventConditions>\r\n"
+#define QUERY_EVENTS_OREC_END          "\t\t</orEventConditions>\r\n"
+#define QUERY_EVENTS_ORJC_BEGIN                "\t\t<orJobConditions>\r\n"
+#define QUERY_EVENTS_ORJC_END          "\t\t</orJobConditions>\r\n"
+#define QUERY_JOBS_REQUEST_BEGIN       "<edg_wll_QueryJobsRequest"
+#define QUERY_JOBS_REQUEST_END         "\t</and>\r\n</edg_wll_QueryJobsRequest>"
+#define QUERY_JOBS_OR_BEGIN            "\t\t<orJobConditions>\r\n"
+#define QUERY_JOBS_OR_END              "\t\t</orJobConditions>\r\n"
+#define PURGE_REQUEST_BEGIN            "<edg_wll_PurgeRequest>\r\n"
+#define PURGE_REQUEST_END              "</edg_wll_PurgeRequest>\r\n"
+#define DUMP_REQUEST_BEGIN             "<edg_wll_DumpRequest>\r\n"
+#define DUMP_REQUEST_END               "</edg_wll_DumpRequest>\r\n"
+#define LOAD_REQUEST_BEGIN             "<edg_wll_LoadRequest>\r\n"
+#define LOAD_REQUEST_END               "</edg_wll_LoadRequest>\r\n"
+#define INDEXED_ATTRS_REQUEST_BEGIN    "<edg_wll_IndexedAttrsReguest>\r\n"
+#define INDEXED_ATTRS_REQUEST_END      "</edg_wll_IndexedAttrsReguest>\r\n"
+#define NOTIF_REQUEST_BEGIN            "<edg_wll_NotifRequest"
+#define NOTIF_REQUEST_END              "</edg_wll_NotifRequest>\r\n"
+
+
+/* lists of accepted tags */
+static const char * const jobStatTags[] = {
+@@@{
+       selectType $status '_common_';
+        for (getFieldsOrdered $status) {
+                gen "\t\"$_\",\n";
+        }
+@@@}
+        NULL
+};
+
+
+@@@{
+                gen "static const char * const eventJobCommon\[] = {";
+                selectType $event '_common_';
+                for (getFieldsOrdered $event) {
+                        my $f = selectField $event $_;
+                        my $fn = $f->{name};
+                        gen "\"$fn\",  ";
+                }
+                gen "NULL };\n";
+@@@}
+@@@{
+        for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+                        $event->getTypes) {
+                gen "static const char * const event$t\[] = {";
+                selectType $event $t;
+                for (getFieldsOrdered $event) {
+                        my $f = selectField $event $_;
+                        my $fn = $f->{name};
+                        gen "\"$fn\",  "
+                }
+                gen "NULL };\n";
+        }
+@@@}
+
+
+
+static const char * const * const eventTags[] = {
+        eventJobCommon,
+@@@{
+        for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} }
+                        $event->getTypes) {
+                gen "\tevent$t,\n";
+        }
+        gen "\tNULL\n};\n";
+@@@}
+
+
+
+/* used when error unrecoverable*/
+#define unexpError() {\
+        char    *e;\
+\
+        if (XMLCtx->errtxt) {\
+                asprintf(&e,"%s\nunexpected <%s> at line %d",XMLCtx->errtxt,\
+                        XMLCtx->element,XML_GetCurrentLineNumber(XMLCtx->p));\
+                free(XMLCtx->errtxt);\
+        } else asprintf(&e,"unexpected <%s> at line %d",\
+                XMLCtx->element,XML_GetCurrentLineNumber(XMLCtx->p));\
+        XMLCtx->errtxt = e;\
+}
+
+
+
+static void emptyCall(void){
+}
+
+/* used when error recoverable */
+// XXX quadratic complexity - problematic when lots of errors in long outputs (10.000's)
+#define unexpWarning() {\
+        char    *e;\
+\
+emptyCall(); \
+        if (XMLCtx->warntxt) {\
+                asprintf(&e,"%s\nunexpected <%s> at line %d",XMLCtx->warntxt,\
+                        XMLCtx->element,XML_GetCurrentLineNumber(XMLCtx->p));\
+                free(XMLCtx->warntxt);\
+        } else asprintf(&e,"unexpected <%s> at line %d",\
+                XMLCtx->element,XML_GetCurrentLineNumber(XMLCtx->p));\
+        XMLCtx->warntxt = e;\
+}
+
+
+
+
+static void startQueryJobs(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+        
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp("edg_wll_QueryJobsResult", el)) { unexpError() break;}
+                        if (attr[0] && attr[1] && attr[2] && attr[3]) {
+                               if (strcmp(attr[0],"code")) { unexpError() break;}
+                                else XMLCtx->errCode = atoi(attr[1]);
+
+                                if (strcmp(attr[2],"desc")) { unexpError() break;}
+                                else XMLCtx->errDesc = strdup(attr[3]);
+                         }
+                        break;  
+
+               case 1 : if (strcmp("edg_wll_Job", el)) unexpError()
+                        break;
+
+               case 2 : if (!strcmp("jobId", el)) {
+                               /* allocates space only for pointers to structures edg_wlc_jobid_t */
+                               /* space for structures allocate characterUserJobs         */
+                               XMLCtx->jobsOutGlobal = realloc(XMLCtx->jobsOutGlobal,
+                                                        (XMLCtx->position+1)*sizeof(*XMLCtx->jobsOutGlobal));
+                               if (!XMLCtx->jobsOutGlobal) { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                               XMLCtx->jobsOutGlobal[XMLCtx->position] = NULL;
+                        }
+                        else if (!strcmp("jobStat", el)) {
+                               XMLCtx->stat_begin = XML_GetCurrentByteIndex(XMLCtx->p);
+                               XMLCtx->jobStatGlobal = (edg_wll_JobStat *) realloc(XMLCtx->jobStatGlobal,
+                                                               (XMLCtx->position2+1)*sizeof(*XMLCtx->jobStatGlobal));
+
+                        }
+                        else unexpWarning()
+                        break;
+
+               // XXX ?? this may be usefull with status
+               case 3 : /* fall through */
+               case 4 : /* do not check xml tags, try to be faul-tolerant              */
+                        /* if tag not found during unparsing, xmlMalformed flag is set */
+                        break;
+
+               default: if (!XMLCtx->stat_begin) unexpWarning()
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+
+static void startQueryEvents(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+        
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp("edg_wll_QueryEventsResult", el)) { unexpError() break;}
+                         if (attr[0] && attr[1] && attr[2] && attr[3]) {
+                                if (strcmp(attr[0],"code")) { unexpError() break;}
+                                else XMLCtx->errCode = atoi(attr[1]);
+
+                                if (strcmp(attr[2],"desc")) { unexpError() break;}
+                                else XMLCtx->errDesc = strdup(attr[3]);
+                         }
+
+                        break;  
+
+               case 1 : if (strcmp("edg_wll_Event", el)) unexpError()
+                         else {
+                               XMLCtx->position++;
+                               
+                               if (!attr[0] || !attr[1]) { unexpError() break;}
+                               if (strcmp(attr[0],"name")) { unexpError() break;}
+                                if ( (XMLCtx->eventCode = edg_wll_StringToEvent((char *) attr[1])) 
+                                       == EDG_WLL_EVENT_UNDEF ) { unexpError() break;}
+                                XMLCtx->eventsOutGlobal = realloc(XMLCtx->eventsOutGlobal,
+                                                         (XMLCtx->position+1)*sizeof(*XMLCtx->eventsOutGlobal));
+                                       if (!XMLCtx->eventsOutGlobal) { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                                       memset(&(XMLCtx->eventsOutGlobal)[XMLCtx->position],0,sizeof(*XMLCtx->eventsOutGlobal));
+                                XMLCtx->eventsOutGlobal[XMLCtx->position].any.type  = XMLCtx->eventCode;
+                               }
+                        break;
+
+               case 2 : /* do not check xml tags, try to be faul-tolerant              */
+                        /* if tag not found during unparsing, xmlMalformed flag is set */
+                        break;
+
+               default: unexpWarning()
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+
+
+static void startUserJobs(void *data, const char *el, const char **attr UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp("edg_wll_UserJobs", el)) { unexpError() break;}
+                         if (attr[0] && attr[1] && attr[2] && attr[3]) {
+                                if (strcmp(attr[0],"code")) { unexpError() break;}
+                                else XMLCtx->errCode = atoi(attr[1]);
+
+                                if (strcmp(attr[2],"desc")) { unexpError() break;}
+                               else XMLCtx->errDesc = strdup(attr[3]);
+                         }
+                        break;  
+
+               case 1 : if (strcmp("jobId", el)) unexpError()
+                        else {
+                               /* allocates space only for pointers to structures edg_wlc_jobid_t */
+                               /* space for structures allocate endUserJobs                       */
+                               XMLCtx->jobsOutGlobal = realloc(XMLCtx->jobsOutGlobal,
+                                                        (XMLCtx->position+1)*sizeof(XMLCtx->jobsOutGlobal));
+                               if (!XMLCtx->jobsOutGlobal) { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                               XMLCtx->jobsOutGlobal[XMLCtx->position] = NULL;
+                        }
+                        break;
+
+
+               default: unexpWarning()
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+
+static void startJobStatus(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       edg_wll_JobStatCode     statusCode;             /* code of status in process */
+
+
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp("jobStat", el)) { unexpError() break;}
+                        if (!attr[0] || !attr[1]) { unexpError() break;}
+                        if (strcmp(attr[0],"name")) { unexpError() break;}
+
+                        if ( (statusCode = edg_wll_StringToStat(attr[1])) 
+                               == (edg_wll_JobStatCode)-1 ) 
+                               /* status unknown, but try to get as much as possible */
+                               unexpWarning()
+                        else {
+                               if (edg_wll_InitStatus(&XMLCtx->jobStatSingleGlobal)) 
+                                       { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                               XMLCtx->jobStatSingleGlobal.state = statusCode;
+                        }
+
+                         if (attr[2] && attr[3] && attr[4] && attr[5]) {
+                                if (strcmp(attr[2],"code")) { unexpError() break;}
+                                else XMLCtx->errCode = atoi(attr[3]);
+
+                                if (strcmp(attr[4],"desc")) { unexpError() break;}
+                                else XMLCtx->errDesc = strdup(attr[5]);
+                         }
+                         break;
+
+               case 1 : if (!strcmp("user_tags", el) || !strcmp("user_values", el) 
+                               || !strcmp("children_hist", el) ||  !strcmp("stateEnterTimes", el)
+                               || !strcmp("children_states", el) || !strcmp("children", el)) {
+                               XMLCtx->stat_begin = XML_GetCurrentByteIndex(XMLCtx->p);
+                        }
+                        break;
+
+               case 2 : /* fall through */
+               case 3 : /* fall through */
+               case 4 : /* do not check xml tags, try to be faul-tolerant              */
+                        /* if tag not found during unparsing, xmlMalformed flag is set */
+                        break;
+
+
+               default: unexpWarning() 
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+static void startStrList(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp(XMLCtx->XML_tag, el)) unexpError()
+                        break;  
+
+               case 1 : if (!strcmp(XMLCtx->XML_tag2, el)) {
+                               XMLCtx->strListGlobal = realloc(XMLCtx->strListGlobal,
+                                                        (XMLCtx->position+1)*sizeof(*XMLCtx->strListGlobal));
+                                if (!XMLCtx->strListGlobal) { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                                XMLCtx->strListGlobal[XMLCtx->position] = NULL;
+
+                        }
+                        break;
+
+               default: unexpWarning() 
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+
+static void startIntList(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       int index = 0;
+
+
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp(XMLCtx->XML_tag, el)) unexpError()
+                        break;  
+
+               case 1 : if ( (index = XMLCtx->tagToIndex(el)) >= 0 ) {
+                               if (index > XMLCtx->max_index) {
+                                       XMLCtx->max_index = index;
+                                       /* leave zeros' possition for array length */
+                                       XMLCtx->intListGlobal = realloc(XMLCtx->intListGlobal,
+                                                        (XMLCtx->max_index+2)*sizeof(*XMLCtx->intListGlobal));
+                                      if (!XMLCtx->intListGlobal) {edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                                       XMLCtx->intListGlobal[XMLCtx->max_index+1] = 0;
+                               }
+
+                        }
+                        break;
+
+               default: unexpWarning() 
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+static void startTagList(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp(XMLCtx->XML_tag, el)) unexpError()
+                        break;  
+
+               case 1 : if (!attr[0] || !attr[1]) { unexpError() break;}
+                        if (strcmp(attr[0],"name")) { unexpError() break;}
+
+                        if (!strcmp(XMLCtx->XML_tag2, el)) {
+                               XMLCtx->tagListGlobal = realloc(XMLCtx->tagListGlobal,
+                                                        (XMLCtx->position+1)*sizeof(*XMLCtx->tagListGlobal));
+                                if (!XMLCtx->tagListGlobal) { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                               XMLCtx->tagListGlobal[XMLCtx->position].tag = strdup(attr[1]);
+                               XMLCtx->stat_begin = XML_GetCurrentByteIndex(XMLCtx->p);
+                        }
+                        break;
+
+               default: unexpWarning() 
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+static void startStsList(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       if (XMLCtx->char_buf) edg_wll_freeBuf(XMLCtx);
+
+        strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+                case 0 : if (strcmp(XMLCtx->XML_tag, el)) unexpError()
+                        break;  
+
+               case 1 : if (!strcmp(XMLCtx->XML_tag2, el)) {
+                               XMLCtx->stsListGlobal = realloc(XMLCtx->stsListGlobal,
+                                                        (XMLCtx->position+1)*sizeof(*XMLCtx->stsListGlobal));
+                                if (!XMLCtx->stsListGlobal) { edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); unexpError() return; }
+                                // edg_wll_InitStatus(&XMLCtx->stsListGlobal[XMLCtx->position]);
+                               // initialized in startJobStatus();
+
+                               XMLCtx->stat_begin = XML_GetCurrentByteIndex(XMLCtx->p);
+                        }
+                        break;
+
+               case 2 : /* fall through */
+               case 3 : /* fall through */
+               case 4 : /* fall through */
+               case 5 : /* do not check xml tags ut to depth 5 */
+                        /* will be checked later               */
+                        break;
+
+               default: unexpWarning() 
+                        break;
+       }
+
+       XMLCtx->level++;
+}
+
+
+
+static void startPurgeResult(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       int     i;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_PurgeResult")) { unexpError() break;}
+                       for ( i = 0; attr[i] && attr[i+1]; i += 2 ) {
+                               if (!strcmp(attr[i],"code"))
+                                       XMLCtx->errCode = atoi(attr[i+1]);
+                               else if (!strcmp(attr[i],"desc"))
+                                       XMLCtx->errDesc = strdup(attr[i+1]);
+                               else { unexpError() }
+                       }
+                       break;
+               case 1: if (strcasecmp(el,"jobs") && strcasecmp(el,"server_file")) unexpWarning()
+                       break;
+               case 2: if (!strcasecmp(el,"jobId")) {
+                               XMLCtx->purgeResultGlobal.jobs = realloc(XMLCtx->purgeResultGlobal.jobs,
+                                               (XMLCtx->position+2) * sizeof(XMLCtx->purgeResultGlobal.jobs));
+
+                                if (!XMLCtx->purgeResultGlobal.jobs) { 
+                                       edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); 
+                                       unexpError() return;
+                               }
+                                XMLCtx->purgeResultGlobal.jobs[XMLCtx->position+1] = NULL;
+                       }
+                       else 
+                               unexpWarning()
+                       break;
+               default: unexpWarning() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startDumpResult(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       int     i;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_DumpResult")) { unexpError() break;}
+                       for ( i = 0; attr[i] && attr[i+1]; i += 2 ) {
+                               if (!strcmp(attr[i],"code"))
+                                       XMLCtx->errCode = atoi(attr[i+1]);
+                               else if (!strcmp(attr[i],"desc"))
+                                       XMLCtx->errDesc = strdup(attr[i+1]);
+                               else { unexpError() }
+                       }
+                       break;
+               case 1: if (strcasecmp(el,"from") && strcasecmp(el,"to") 
+                               && strcasecmp(el,"server_file")) unexpWarning()
+                       break;
+               default: unexpWarning() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startLoadResult(void *data, const char *el, const char **attr)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        int     i;
+
+        
+        strcpy(XMLCtx->element, el);
+        
+        switch (XMLCtx->level) {
+                case 0: if (strcasecmp(el,"edg_wll_LoadResult")) { unexpError() break;}
+                        for ( i = 0; attr[i] && attr[i+1]; i += 2 ) {
+                                if (!strcmp(attr[i],"code"))
+                                        XMLCtx->errCode = atoi(attr[i+1]);
+                                else if (!strcmp(attr[i],"desc"))
+                                        XMLCtx->errDesc = strdup(attr[i+1]);
+                                else { unexpError() }
+                        }       
+                        break;          
+                case 1: if (strcasecmp(el,"from") && strcasecmp(el,"to")
+                                && strcasecmp(el,"server_file")) unexpWarning()
+                        break;  
+                default: unexpWarning()
+                         break;
+        }       
+        XMLCtx->level++;
+}
+
+
+
+static void startIndexedAttrs(void *data, const char *el, const char **attr)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        int     i;
+
+        
+        strcpy(XMLCtx->element, el);
+        
+        switch (XMLCtx->level) {
+                case 0: if (strcasecmp(el,"edg_wll_GetIndexedAttributesResult")) { unexpError() break;}
+                        for ( i = 0; attr[i] && attr[i+1]; i += 2 ) {
+                                if (!strcmp(attr[i],"code"))
+                                        XMLCtx->errCode = atoi(attr[i+1]);
+                                else if (!strcmp(attr[i],"desc"))
+                                        XMLCtx->errDesc = strdup(attr[i+1]);
+                                else { unexpError() }
+                        }       
+                        break;          
+                case 1: if (!strcasecmp(el,"index")) {
+                               XMLCtx->attrsGlobal = realloc(XMLCtx->attrsGlobal,
+                                                        (XMLCtx->position+2)*sizeof(*XMLCtx->attrsGlobal));
+                               if (!XMLCtx->attrsGlobal) { 
+                                       edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); 
+                                       unexpError() 
+                                       return;
+                               }
+                               XMLCtx->attrsGlobal[XMLCtx->position] = NULL;
+                               XMLCtx->attrsGlobal[XMLCtx->position+1] = NULL;
+                       }
+                       else
+                                unexpWarning()
+                        break;  
+                case 2: if (!strcasecmp(el,"QueryRec")) {
+                               XMLCtx->attrsGlobal[XMLCtx->position] = 
+                                       realloc(XMLCtx->attrsGlobal[XMLCtx->position],
+                                               (XMLCtx->position2+2)*sizeof(**XMLCtx->attrsGlobal));
+                               if (!XMLCtx->attrsGlobal[XMLCtx->position]) { 
+                                       edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL);
+                                       unexpError()
+                                       return;
+                               }
+                               memset(&(XMLCtx->attrsGlobal[XMLCtx->position][XMLCtx->position2]), 0, 
+                                       2*sizeof(**XMLCtx->attrsGlobal));
+                       }
+                       else
+                                unexpWarning()
+                        break;  
+               case 3: if (strcasecmp(el,"attribute") && strcasecmp(el,"state") &&
+                               strcasecmp(el,"name")) unexpWarning()
+                       break;  
+                default: unexpWarning()
+                         break;
+        }       
+        XMLCtx->level++;
+}
+
+
+
+static void startNotifResult(void *data, const char *el, const char **attr)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        int     i;
+
+        
+        strcpy(XMLCtx->element, el);
+        
+        switch (XMLCtx->level) {
+                case 0: if (strcasecmp(el,"edg_wll_NotifResult")) { unexpError() break;}
+                        for ( i = 0; attr[i] && attr[i+1]; i += 2 ) {
+                                if (!strcmp(attr[i],"code"))
+                                        XMLCtx->errCode = atoi(attr[i+1]);
+                                else if (!strcmp(attr[i],"desc"))
+                                        XMLCtx->errDesc = strdup(attr[i+1]);
+                                else { unexpError() }
+                        }       
+                        break;          
+                case 1: if (strcasecmp(el,"validity")) unexpWarning()
+                        break;  
+                default: unexpWarning()
+                         break;
+        }       
+        XMLCtx->level++;
+}               
+
+
+
+static void char_handler(void *data, const char *s, int len)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+        int i, found = -1, temp_len1;
+        char *temp_s, *temp_s1;
+
+
+        /* if date are only spaces, t\, \r, \n ... don't bother with them */
+        for (i=0; i<len; i++)
+                        if (!isspace(s[i])) { found = i; break; }
+        if (found == -1) return;
+
+        temp_s = malloc((len+1) * sizeof(char));
+
+        /* otherwise use them */
+        memcpy(temp_s,s,len);
+        temp_s[len] = 0;
+        temp_s1 = edg_wll_UnescapeXML((const char *) temp_s);
+        temp_len1 = strlen(temp_s1);
+
+        if (XMLCtx->char_buf_len) 
+               XMLCtx->char_buf = realloc(XMLCtx->char_buf,XMLCtx->char_buf_len+temp_len1 + 1);
+        else  
+               XMLCtx->char_buf = (char *) malloc(temp_len1 + 1);
+
+       memcpy(XMLCtx->char_buf+XMLCtx->char_buf_len,temp_s1,temp_len1 + 1);
+               XMLCtx->char_buf_len += temp_len1;
+        free(temp_s1);
+        free(temp_s);
+}
+
+
+
+static void endQueryJobs(void *data, const char *el)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       switch (XMLCtx->level) {
+               case 3 :
+                       if (!strcmp(XMLCtx->element,"jobId")) {
+                               XMLCtx->jobsOutGlobal[XMLCtx->position] =
+                                       edg_wll_from_string_to_jobid(XMLCtx);
+                               XMLCtx->position++;
+                       }
+                       else if (!strcmp(el,"jobStat")) {
+                               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                                                -  XMLCtx->stat_begin;
+
+                               edg_wll_ParseJobStat(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len, 
+                                                       &XMLCtx->jobStatGlobal[XMLCtx->position2]);
+                               XMLCtx->position2++;
+                               XMLCtx->stat_begin = 0;
+                       }
+                       else {
+                               /* tag was not found -> either missing in previous code or unknown      */
+                               unexpWarning()
+                               edg_wll_freeBuf(XMLCtx);
+                       }
+                       break;
+               default:
+                       if (XMLCtx->char_buf) {
+//                             unexpWarning()
+                               edg_wll_freeBuf(XMLCtx);
+                       }
+                       break;
+       }
+
+        XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+        XMLCtx->level--;
+}
+
+
+static void endQueryEvents(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       
+
+       switch (XMLCtx->level) {
+         case 3: 
+@@@{
+        my $i = 1;
+       my $bi = "\t   ";
+        for my $t (sort {$a cmp $b} getAllFields $event) {
+                if ($i == 1)
+                        { gen "$bi if (!strcmp(XMLCtx->element,\"$t\"))\n"; }
+                else
+                        { gen "$bi else if (!strcmp(XMLCtx->element,\"$t\"))\n"; }
+                $i++;
+                my @fo = sort $event->getFieldOccurence($t);
+                if ($#fo == 0) {
+                        selectType $event $fo[0];
+                        my $f = selectField $event $t;
+                        my $ft = $f->{type};
+                       $fo[0] = $fo[0] eq '_common_' ? 'any' : lcfirst $fo[0];
+                        gen "$bi   XMLCtx->eventsOutGlobal[XMLCtx->position].$fo[0].$t =\n";
+                        gen "$bi \tedg_wll_from_string_to_$ft(XMLCtx);\n";
+                }
+                else {
+                        gen "\t      switch (XMLCtx->eventsOutGlobal[XMLCtx->position].any.type) {\n";
+                       for (@fo) {
+                               selectType $event $_;
+                               my $f = selectField $event $t;
+                               my $ft = $f->{type};
+                               $t = 'any' if $_ eq '_common_';
+                               my $u = uc $_;
+                               $_ = lcfirst $_;
+                               gen "$bi     case EDG_WLL_EVENT_$u :\n";
+                               gen "$bi \t  XMLCtx->eventsOutGlobal[XMLCtx->position].$_.$t =\n";
+                               gen "$bi \t    edg_wll_from_string_to_$ft(XMLCtx);\n";
+                               gen "$bi \t  break;\n";
+
+                       }
+                       gen "$bi       default : { unexpWarning() edg_wll_freeBuf(XMLCtx); } \n";
+                       gen "$bi     }\n";
+                }
+        }
+@@@}
+
+           else {
+               /* tag was not found -> either missing in previous code or unknown      */
+               unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+         default:
+           if (XMLCtx->char_buf) {
+               unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break; 
+       }
+
+        XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+        XMLCtx->level--;
+
+}
+
+
+
+// XXX: endUserJobs should be unusable soon, delete it
+static void endUserJobs(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       int i;
+
+
+       switch (XMLCtx->level) {
+               case 2 :
+                       if (!strcmp(XMLCtx->element,"jobId")) {
+                               XMLCtx->jobsOutGlobal[XMLCtx->position] =
+                                       edg_wll_from_string_to_jobid(XMLCtx);
+                               XMLCtx->position++;
+                       }
+                       else {
+                               /* tag was not found -> either missing in previous code or unknown      */
+                               unexpWarning()
+                               edg_wll_freeBuf(XMLCtx);
+                       }
+                       break;
+               default:
+                       /* only level 2 tags should contain  text fields    */
+                       for (i=0; i < XMLCtx->char_buf_len; i++) 
+                               if (!isspace(XMLCtx->char_buf[i])) unexpWarning()
+                       edg_wll_freeBuf(XMLCtx);
+                       break;
+       }
+
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+       // if ( !strcmp(XMLCtx->element,"jobId") ) XMLCtx->position++;
+       // uncomment this if you want to remove XMLCtx->position++; 5 lines above
+       // it should be useful in case of automatic generation
+        XMLCtx->level--;
+}
+
+
+
+static void endJobStat(void *data, const char *el)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       switch (XMLCtx->level) {
+         case 2 :
+
+@@@{
+        my $i = 1;
+       my $bi = "\t   ";
+       selectType $status '_common_';
+        for my $t (getFieldsOrdered $status) {
+               my $f = selectField $status $t;
+               my $ft = $f->{type};
+               next if defined($f->{special}) && $f->{special} eq 'XMLstructured';
+                if ($i == 1)
+                        { gen "$bi if (!strcmp(XMLCtx->element,\"$t\"))\n"; }
+                else
+                        { gen "$bi else if (!strcmp(XMLCtx->element,\"$t\"))\n"; }
+                $i++;
+               gen "$bi   XMLCtx->jobStatSingleGlobal.$t =\n";
+               gen "$bi     edg_wll_from_string_to_$ft(XMLCtx);\n";
+        }
+@@@}
+           else if (!strcmp(el,"children_hist")) {
+               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                               -  XMLCtx->stat_begin;
+
+               edg_wll_ParseIntList(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len,
+                       "children_hist",(int (*)()) edg_wll_StringToStat, &XMLCtx->jobStatSingleGlobal.children_hist);
+               XMLCtx->stat_begin = 0;
+           }
+                   else if (!strcmp(el,"children")) {
+               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                               -  XMLCtx->stat_begin;
+
+               edg_wll_ParseStrList(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len,
+                       "children", "jobId", &XMLCtx->jobStatSingleGlobal.children);
+               XMLCtx->stat_begin = 0;
+            }
+           else if (!strcmp(el,"children_states")) {
+               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                               -  XMLCtx->stat_begin;
+
+               edg_wll_ParseStsList(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len,
+                       "children_states", "jobStat", &XMLCtx->jobStatSingleGlobal.children_states);
+               XMLCtx->stat_begin = 0;
+            }
+           else if (!strcmp(el,"user_tags")) {
+               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                               -  XMLCtx->stat_begin;
+
+               edg_wll_ParseTagList(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len,
+                       "user_tags", "tag", &XMLCtx->jobStatSingleGlobal.user_tags);
+               XMLCtx->stat_begin = 0;
+            }
+           else if (!strcmp(el,"stateEnterTimes")) {
+               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                               -  XMLCtx->stat_begin;
+
+               edg_wll_ParseIntList(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len,
+                       "stateEnterTimes",(int (*)()) edg_wll_StringToStat, &XMLCtx->jobStatSingleGlobal.stateEnterTimes);
+               XMLCtx->stat_begin = 0;
+            }
+           else {
+             /* tag was not found -> either missing in previous code or unknown        */
+             unexpWarning()
+             edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+         default:
+           if (XMLCtx->char_buf) {
+//             unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+       }
+
+       XMLCtx->char_buf = NULL;
+       XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+       XMLCtx->level--;
+}
+
+
+
+static void endStrList(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       switch (XMLCtx->level) {
+         case 2 :
+           if (!strcmp(XMLCtx->element,XMLCtx->XML_tag2)) {
+             XMLCtx->strListGlobal[XMLCtx->position] =
+               edg_wll_from_string_to_string(XMLCtx);
+             XMLCtx->position++;
+           }
+           else {
+             /* tag was not found -> either missing in previous code or unknown        */
+             unexpWarning()
+             edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+         default:
+           if (XMLCtx->char_buf) {
+//             unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+       }
+
+       XMLCtx->char_buf = NULL;
+       XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+       XMLCtx->level--;
+}
+
+
+
+static void endIntList(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       int index;
+
+       
+       switch (XMLCtx->level) {
+         case 2 :
+           if ((index = XMLCtx->tagToIndex(XMLCtx->element)) >= 0 ) {
+             XMLCtx->intListGlobal[index+1] =
+               edg_wll_from_string_to_int(XMLCtx);
+           }
+           else {
+             /* tag was not found -> either missing in previous code or unknown        */
+             unexpWarning()
+             edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+         default:
+           if (XMLCtx->char_buf) {
+//             unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+       }
+
+       XMLCtx->char_buf = NULL;
+       XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+       XMLCtx->level--;
+}
+
+
+static void endTagList(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       switch (XMLCtx->level) {
+         case 2 :
+           if (!strcmp(XMLCtx->element,XMLCtx->XML_tag2)) {
+             XMLCtx->tagListGlobal[XMLCtx->position].value =
+               edg_wll_from_string_to_string(XMLCtx);
+             XMLCtx->position++;
+           }
+           else {
+             /* tag was not found -> either missing in previous code or unknown        */
+             unexpWarning()
+             edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+         default:
+           if (XMLCtx->char_buf) {
+//             unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+       }
+
+       XMLCtx->char_buf = NULL;
+       XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+       XMLCtx->level--;
+}
+
+
+static void endStsList(void *data, const char *el)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       switch (XMLCtx->level) {
+         case 2 :
+           if (!strcmp(el,XMLCtx->XML_tag2)) {
+               long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                                -  XMLCtx->stat_begin;
+
+               edg_wll_ParseJobStat(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->stat_begin, len, 
+                               &XMLCtx->stsListGlobal[XMLCtx->position]);
+               XMLCtx->stat_begin = 0;
+               XMLCtx->position++;
+           }
+           else {
+             /* tag was not found -> either missing in previous code or unknown        */
+             unexpWarning()
+             edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+         default:
+           if (XMLCtx->char_buf) {
+//             unexpWarning()
+               edg_wll_freeBuf(XMLCtx);
+           }
+           break;
+       }
+
+       XMLCtx->char_buf = NULL;
+       XMLCtx->char_buf_len = 0;
+       memset(&(XMLCtx->element), 0, sizeof(XMLCtx->element));
+       XMLCtx->level--;
+}
+
+
+
+
+static void endPurgeResult(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        char *e;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"server_file")) 
+                       XMLCtx->purgeResultGlobal.server_file = edg_wll_from_string_to_string(XMLCtx);
+       }
+       else if (XMLCtx->level == 3) {
+               if (!strcmp(XMLCtx->element,"jobId")) {
+                       if ( (XMLCtx->purgeResultGlobal.jobs[XMLCtx->position++] = 
+                               edg_wll_from_string_to_string(XMLCtx)) == NULL )
+                       {
+                               if (XMLCtx->errtxt) {
+                                       asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                       free(XMLCtx->errtxt);
+                               } else asprintf(&e,"%s: invalid JobId at line %d",
+                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                               XMLCtx->errtxt = e;
+                       }
+               }
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+static void endDumpResult(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"server_file")) 
+                       XMLCtx->dumpResultGlobal.server_file = edg_wll_from_string_to_string(XMLCtx);
+               else if (!strcmp(XMLCtx->element,"from")) {
+                       if (isdigit(XMLCtx->char_buf[0]))
+                       XMLCtx->dumpResultGlobal.from = edg_wll_from_string_to_time_t(XMLCtx);
+                       else
+                               XMLCtx->dumpResultGlobal.from = edg_wll_StringToDumpConst(XMLCtx->char_buf);
+               }
+               else if (!strcmp(XMLCtx->element,"to")) {
+                       if (isdigit(XMLCtx->char_buf[0]))
+                       XMLCtx->dumpResultGlobal.to = edg_wll_from_string_to_time_t(XMLCtx);
+                       else
+                               XMLCtx->dumpResultGlobal.to = edg_wll_StringToDumpConst(XMLCtx->char_buf);
+               }
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+static void endLoadResult(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"server_file"))
+                        XMLCtx->loadResultGlobal.server_file = edg_wll_from_string_to_string(XMLCtx);
+                else if (!strcmp(XMLCtx->element,"from"))
+                        XMLCtx->loadResultGlobal.from = edg_wll_from_string_to_time_t(XMLCtx);
+                else if (!strcmp(XMLCtx->element,"to"))
+                        XMLCtx->loadResultGlobal.to = edg_wll_from_string_to_time_t(XMLCtx);
+               
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+static void endIndexedAttrs(void *data, const char *el)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+        if (XMLCtx->level == 2) {
+               if (!strcmp(el,"index"))
+                       XMLCtx->position++;
+                       XMLCtx->position2 = 0;
+       }
+        if (XMLCtx->level == 3) {
+               if (!strcmp(el,"QueryRec"))
+                       XMLCtx->position2++;
+       }
+       if (XMLCtx->level == 4) {
+                if (!strcmp(XMLCtx->element,"attribute")) {
+                        XMLCtx->attrsGlobal[XMLCtx->position][XMLCtx->position2].attr = 
+                               edg_wll_StringToquery_attr(edg_wll_from_string_to_string(XMLCtx));
+               }
+               else if (!strcmp(XMLCtx->element,"state")) {
+                       XMLCtx->attrsGlobal[XMLCtx->position][XMLCtx->position2].attr_id.state =
+                               edg_wll_StringToStat(edg_wll_from_string_to_string(XMLCtx));
+               }
+               else if (!strcmp(XMLCtx->element,"name")) {
+                       XMLCtx->attrsGlobal[XMLCtx->position][XMLCtx->position2].attr_id.tag =
+                               edg_wll_from_string_to_string(XMLCtx);
+               }       
+        }
+
+        XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+        XMLCtx->level--;
+}
+
+
+static void endNotifResult(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"validity"))
+                        XMLCtx->notifValidity = edg_wll_from_string_to_time_t(XMLCtx);
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+#undef unexpError
+#undef unexpWarning
+
+
+
+edg_wll_ErrorCode edg_wll_ParseQueryJobs(edg_wll_Context ctx, char *messageBody, edg_wlc_JobId **jobsOut, edg_wll_JobStat **statesOut)
+{
+       int i;
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode = 0;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+
+       /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+       XML_SetElementHandler(XMLCtx.p, startQueryJobs, endQueryJobs);
+       XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+       /* parse messageBody */
+       if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+               char *errorMessage;
+
+               asprintf(&errorMessage, "XML parse error at line %d:\n%s\n",
+                       XML_GetCurrentLineNumber(XMLCtx.p),
+                       XML_ErrorString(XML_GetErrorCode(XMLCtx.p))); 
+
+               edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);            
+       }  else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               for (i=0; i<XMLCtx.position; i++)  edg_wlc_JobIdFree( (XMLCtx.jobsOutGlobal)[i] );
+               free(XMLCtx.jobsOutGlobal);
+               XMLCtx.jobsOutGlobal = NULL;
+               XMLCtx.position = 0;
+               if (jobsOut) *jobsOut = NULL;   
+
+               for (i=0; i<XMLCtx.position2; i++) edg_wll_FreeStatus( &(XMLCtx.jobStatGlobal[i]) );
+               free(XMLCtx.jobStatGlobal);
+               XMLCtx.jobStatGlobal = NULL;
+               XMLCtx.position2 = 0;
+               if (statesOut) *statesOut = NULL;
+
+                if (XMLCtx.errDesc) {
+                        free(XMLCtx.errDesc);
+                        XMLCtx.errDesc = NULL;
+                        XMLCtx.errCode = 0;
+                }
+        }
+
+       /* malloc-ate one more row for NULL list termination */
+        XMLCtx.jobsOutGlobal = realloc(XMLCtx.jobsOutGlobal,
+                               (XMLCtx.position+1)*sizeof(*XMLCtx.jobsOutGlobal));
+
+        if (!XMLCtx.jobsOutGlobal) { 
+               errorCode = (edg_wll_ErrorCode) ENOMEM;
+               if (jobsOut) *jobsOut = NULL;   
+       }
+       else {
+               /* add NULL to end of list */
+               XMLCtx.jobsOutGlobal[XMLCtx.position] = NULL;           
+
+               /* return results */
+               // XXX : may be should not be transfered; need to change protocol and 
+               // XXX   then return only non-NULL jobsOut or statesOut
+               if (jobsOut) 
+                       *jobsOut = XMLCtx.jobsOutGlobal;
+               else {
+                       for (i=0; i<XMLCtx.position; i++)  edg_wlc_JobIdFree( (XMLCtx.jobsOutGlobal)[i] );
+                       free(XMLCtx.jobsOutGlobal); 
+               }
+               XMLCtx.jobsOutGlobal = NULL; 
+       }
+       
+       XMLCtx.jobStatGlobal = realloc(XMLCtx.jobStatGlobal,
+                               (XMLCtx.position2+1)*sizeof(*XMLCtx.jobStatGlobal));
+
+       if (!XMLCtx.jobStatGlobal) { 
+               errorCode = (edg_wll_ErrorCode) ENOMEM;
+               if (statesOut) *statesOut = NULL;
+       }
+       else {
+               /* add NULL to end of list */
+               edg_wll_InitStatus(&XMLCtx.jobStatGlobal[XMLCtx.position2]);            
+
+               /* return results */
+               if (statesOut)
+                       *statesOut = XMLCtx.jobStatGlobal;
+               else {
+                       for (i=0; i<XMLCtx.position2; i++) edg_wll_FreeStatus( &(XMLCtx.jobStatGlobal[i]) );
+                       free(XMLCtx.jobStatGlobal);
+               }
+       
+               XMLCtx.jobStatGlobal = NULL; 
+       }
+       
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+               ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+       }
+
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+       /* free parser */
+       XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return errorCode;
+}
+
+
+
+edg_wll_ErrorCode edg_wll_ParseQueryEvents(edg_wll_Context ctx, char *messageBody, edg_wll_Event **eventsOut)
+{
+       int i;
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode = 0;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.position = -1;
+       edg_wll_ResetError(ctx);        
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startQueryEvents, endQueryEvents);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "XML parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+               edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+               free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               *eventsOut = NULL;
+               for (i=0; i<= XMLCtx.position; i++) edg_wll_FreeEvent( &((XMLCtx.eventsOutGlobal)[i]) );
+                       free(XMLCtx.eventsOutGlobal); 
+               XMLCtx.eventsOutGlobal = NULL;
+
+                if (XMLCtx.errDesc) {
+                        free(XMLCtx.errDesc);
+                        XMLCtx.errDesc = NULL;
+                        XMLCtx.errCode = 0;
+                }
+       }
+        else {
+               /* malloc-ate one more row for NULL list termination */
+                XMLCtx.eventsOutGlobal = realloc(XMLCtx.eventsOutGlobal,
+                                          (++XMLCtx.position+1)*sizeof(*XMLCtx.eventsOutGlobal));
+                if (!XMLCtx.eventsOutGlobal) { 
+                       errorCode = (edg_wll_ErrorCode) ENOMEM; 
+                       *eventsOut = NULL;
+               }
+               else {
+                       /* last event in list is EDG_WLL_EVENT_UNDEF for end-of-list detection */
+                       memset(&XMLCtx.eventsOutGlobal[XMLCtx.position],0,sizeof(*XMLCtx.eventsOutGlobal));
+                       XMLCtx.eventsOutGlobal[XMLCtx.position].any.type = EDG_WLL_EVENT_UNDEF;
+
+                       /* return results */
+                       *eventsOut = XMLCtx.eventsOutGlobal;
+                       XMLCtx.eventsOutGlobal = NULL; 
+               }
+        }
+       
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+                ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+        }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"------------------------edg_wll_ParseQueryEvents----------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);
+        return errorCode;
+}
+
+
+
+
+edg_wll_ErrorCode edg_wll_ParseUserJobs(edg_wll_Context ctx, char *messageBody, edg_wlc_JobId **jobsOut)
+{
+       int i;
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode = 0;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+
+       /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+       XML_SetElementHandler(XMLCtx.p, startUserJobs, endUserJobs);
+       XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+       /* parse messageBody */
+       if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+               char *errorMessage;
+
+
+               asprintf(&errorMessage, "XML parse error at line %d:\n%s\n",
+                       XML_GetCurrentLineNumber(XMLCtx.p),
+                       XML_ErrorString(XML_GetErrorCode(XMLCtx.p))); 
+               *jobsOut = NULL;        
+
+               edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);            
+       }  else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+                *jobsOut = NULL;
+               for (i=0; i<XMLCtx.position; i++)  edg_wlc_JobIdFree( (XMLCtx.jobsOutGlobal)[i] );
+               free(XMLCtx.jobsOutGlobal);
+               XMLCtx.jobsOutGlobal = NULL;
+        } else {
+               /* malloc-ate one more row for NULL list termination */
+                XMLCtx.jobsOutGlobal = realloc(XMLCtx.jobsOutGlobal,
+                                        (XMLCtx.position+1)*sizeof(*XMLCtx.jobsOutGlobal));
+                if (!XMLCtx.jobsOutGlobal) { 
+                       errorCode = (edg_wll_ErrorCode) ENOMEM;
+                       *jobsOut = NULL;
+               }
+               else {
+                       /* add NULL to end of list */
+                       XMLCtx.jobsOutGlobal[XMLCtx.position] = NULL;           
+
+                       /* return results */
+                       *jobsOut = XMLCtx.jobsOutGlobal;
+                       XMLCtx.jobsOutGlobal = NULL; 
+               }
+        }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+       /* free parser */
+       XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return errorCode;
+}
+
+
+
+edg_wll_ErrorCode edg_wll_ParseJobStat(edg_wll_Context ctx, char *messageBody, long len, edg_wll_JobStat *stat)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startJobStatus, endJobStat);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, len, 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               edg_wll_FreeStatus( &XMLCtx.jobStatSingleGlobal );
+               memset(stat,0,sizeof(*stat));
+               XMLCtx.position = 0;
+        }
+       else {
+               /* return results */
+               memcpy(stat, &XMLCtx.jobStatSingleGlobal, sizeof(XMLCtx.jobStatSingleGlobal));
+       }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+edg_wll_ErrorCode edg_wll_ParseStrList(edg_wll_Context ctx, char *messageBody, long len, char *tag, char *tag2,  char ***strListOut)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+       asprintf(&XMLCtx.XML_tag,"%s",tag);
+       asprintf(&XMLCtx.XML_tag2,"%s",tag2);
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startStrList, endStrList);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, len, 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               if (XMLCtx.strListGlobal) {
+                       int i;
+
+                       for (i=0; XMLCtx.strListGlobal[i]; i++)
+                               free(XMLCtx.strListGlobal[i]);
+                       free(XMLCtx.strListGlobal);
+                       XMLCtx.strListGlobal = NULL;
+               }
+               *strListOut = NULL;
+               XMLCtx.position = 0;
+        }
+       else {
+               XMLCtx.strListGlobal = realloc(XMLCtx.strListGlobal,
+                               (XMLCtx.position+1)*sizeof(*XMLCtx.strListGlobal));
+
+               if (!XMLCtx.strListGlobal) {
+                       errorCode = (edg_wll_ErrorCode) ENOMEM;
+                       if (strListOut) *strListOut = NULL;
+               }
+                       else {
+                       /* add NULL to end of list */
+                       XMLCtx.strListGlobal[XMLCtx.position] = NULL;
+
+                       /* return results */
+                       *strListOut = XMLCtx.strListGlobal;
+               }
+       }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+edg_wll_ErrorCode edg_wll_ParseIntList(edg_wll_Context ctx, char *messageBody, long len, char *tag, int (*tagToIndex)(),  int **intListOut)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+       asprintf(&XMLCtx.XML_tag,"%s",tag);
+       XMLCtx.tagToIndex = tagToIndex;
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startIntList, endIntList);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, len, 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               if (XMLCtx.intListGlobal) free(XMLCtx.intListGlobal);
+               *intListOut = NULL;
+               XMLCtx.max_index = 0;
+        }
+       else {
+                       /* add array length to zeros' position */
+                       if (XMLCtx.intListGlobal) 
+                       XMLCtx.intListGlobal[0] = XMLCtx.max_index + 1;
+
+               /* return results */
+               *intListOut = XMLCtx.intListGlobal;
+       }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+edg_wll_ErrorCode edg_wll_ParseTagList(edg_wll_Context ctx, char *messageBody, long len, char *tag, char *tag2,  edg_wll_TagValue **tagListOut)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+       asprintf(&XMLCtx.XML_tag,"%s",tag);
+       asprintf(&XMLCtx.XML_tag2,"%s",tag2);
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startTagList, endTagList);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, len, 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               if (XMLCtx.tagListGlobal) {
+                       int i;
+
+                       for (i=0; XMLCtx.tagListGlobal[i].tag; i++) {
+                               free(XMLCtx.tagListGlobal[i].tag);
+                               free(XMLCtx.tagListGlobal[i].value);
+                       }
+                       free(XMLCtx.tagListGlobal);
+                       XMLCtx.tagListGlobal = NULL;
+               }
+               *tagListOut = NULL;
+               XMLCtx.position = 0;
+        }
+       else {
+               XMLCtx.tagListGlobal = realloc(XMLCtx.tagListGlobal,
+                               (XMLCtx.position+1)*sizeof(*XMLCtx.tagListGlobal));
+
+               if (!XMLCtx.tagListGlobal) {
+                       errorCode = (edg_wll_ErrorCode) ENOMEM;
+                       if (tagListOut) *tagListOut = NULL;
+               }
+                       else {
+                       /* add NULL to end of list */
+                       XMLCtx.tagListGlobal[XMLCtx.position].tag = NULL;
+
+                       /* return results */
+                       *tagListOut = XMLCtx.tagListGlobal;
+               }
+       }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+edg_wll_ErrorCode edg_wll_ParseStsList(edg_wll_Context ctx, char *messageBody, long len, char *tag, char *tag2,  edg_wll_JobStat **stsListOut)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+       asprintf(&XMLCtx.XML_tag,"%s",tag);
+       asprintf(&XMLCtx.XML_tag2,"%s",tag2);
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startStsList, endStsList);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, len, 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               if (XMLCtx.stsListGlobal) {
+                       int i;
+
+                       for (i=0; XMLCtx.position; i++)
+                               edg_wll_FreeStatus(&XMLCtx.stsListGlobal[i]);
+                       free(XMLCtx.stsListGlobal);
+                       XMLCtx.stsListGlobal = NULL;
+               }
+               *stsListOut = NULL;
+               XMLCtx.position = 0;
+        }
+       else {
+               XMLCtx.stsListGlobal = realloc(XMLCtx.stsListGlobal,
+                               (XMLCtx.position+1)*sizeof(*XMLCtx.stsListGlobal));
+
+               if (!XMLCtx.stsListGlobal) {
+                       errorCode = (edg_wll_ErrorCode) ENOMEM;
+                       if (stsListOut) *stsListOut = NULL;
+               }
+                       else {
+                       /* add NULL to end of list */
+                       edg_wll_InitStatus(&XMLCtx.stsListGlobal[XMLCtx.position]);
+
+                       /* return results */
+                       *stsListOut = XMLCtx.stsListGlobal;
+               }
+       }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+/* parse purge result from client */
+edg_wll_ErrorCode edg_wll_ParsePurgeResult(edg_wll_Context ctx, char *messageBody, edg_wll_PurgeResult *result)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_ResetError(ctx);
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startPurgeResult, endPurgeResult);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               int i;
+
+               if (XMLCtx.purgeResultGlobal.jobs) {
+                       for (i=0; XMLCtx.purgeResultGlobal.jobs[i]; i++) 
+                               free(XMLCtx.purgeResultGlobal.jobs[i]);
+                       free(XMLCtx.purgeResultGlobal.jobs);
+               }
+               memset(result,0,sizeof(*result));
+               free(XMLCtx.purgeResultGlobal.server_file);
+               
+       } else {
+               memcpy(result, &XMLCtx.purgeResultGlobal, sizeof(XMLCtx.purgeResultGlobal));
+       }
+
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+               ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+       }
+       
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+/* parse dump result from client */
+edg_wll_ErrorCode edg_wll_ParseDumpResult(edg_wll_Context ctx, char *messageBody, edg_wll_DumpResult *result)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_ResetError(ctx);
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startDumpResult, endDumpResult);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               free(XMLCtx.dumpResultGlobal.server_file);
+               memset(result,0,sizeof(*result));
+       } else {
+               memcpy(result, &XMLCtx.dumpResultGlobal, sizeof(XMLCtx.dumpResultGlobal));
+       }
+
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+               ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+       }
+       
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+/* parse load result from client */
+edg_wll_ErrorCode edg_wll_ParseLoadResult(edg_wll_Context ctx, char *messageBody, edg_wll_LoadResult *result)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_ResetError(ctx);
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startLoadResult, endLoadResult);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               free(XMLCtx.loadResultGlobal.server_file);
+               memset(result,0,sizeof(*result));
+       } else {
+               memcpy(result, &XMLCtx.loadResultGlobal, sizeof(XMLCtx.loadResultGlobal));
+       }
+
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+               ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+       }
+       
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+edg_wll_ErrorCode edg_wll_ParseIndexedAttrs(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***attrs)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+
+       edg_wll_initXMLCtx(&XMLCtx);
+       edg_wll_ResetError(ctx);        
+       XMLCtx.message_body = messageBody;
+       XMLCtx.ctx = ctx;
+
+        /* initialize parser */
+       XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startIndexedAttrs, endIndexedAttrs);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+       XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+        /* parse messageBody */
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+        if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               if (XMLCtx.attrsGlobal) {
+                       int i,j;
+
+                       for (i=0; XMLCtx.attrsGlobal[i]; i++) {
+                               for (j=0; XMLCtx.attrsGlobal[i][j].attr != EDG_WLL_QUERY_ATTR_UNDEF; j++) {
+                                       if (XMLCtx.attrsGlobal[i][j].attr == EDG_WLL_QUERY_ATTR_USERTAG) 
+                                               free(XMLCtx.attrsGlobal[i][j].attr_id.tag);
+                               }               
+                               free(XMLCtx.attrsGlobal[i]);
+                       }
+                       free(XMLCtx.attrsGlobal);
+                       XMLCtx.attrsGlobal = NULL;
+               }
+               XMLCtx.position = 0;
+               XMLCtx.position2 = 0;
+        }
+
+       /* return results */
+       *attrs = XMLCtx.attrsGlobal;
+
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+               ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+       }
+
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+/* parse notification result from client */
+edg_wll_ErrorCode edg_wll_ParseNotifResult(edg_wll_Context ctx, char *messageBody, time_t *validity)
+{
+       edg_wll_XML_ctx XMLCtx;
+       edg_wll_ErrorCode errorCode;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_ResetError(ctx);
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startNotifResult, endNotifResult);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((errorCode = edg_wll_Error(ctx,NULL,NULL))) {
+               *validity = -1;
+       } else {
+               *validity = XMLCtx.notifValidity;
+       }
+
+       if (XMLCtx.errDesc || XMLCtx.errCode) {
+               ctx->errDesc = XMLCtx.errDesc;
+                ctx->errCode = XMLCtx.errCode;
+       }
+       
+       /* print all warning if corresponding env variable is set       */
+       if (XMLCtx.warntxt && getenv("EDG_WLL_XML_WARNINGS")) { 
+               fprintf(stderr,"----------------------------------------------------\n");
+               fprintf(stderr,"%s\n\n",XMLCtx.warntxt);
+               fprintf(stderr,"%s\n",messageBody);
+               fprintf(stderr,"----------------------------------------------------\n");
+       }
+
+        /* free parser */
+        XML_ParserFree(XMLCtx.p);
+
+       edg_wll_freeXMLCtx(&XMLCtx);    
+       return errorCode;
+}
+
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_QueryJobs */
+int edg_wll_JobQueryRecToXML(
+               edg_wll_Context ctx,
+               edg_wll_QueryRec const * const *conditions,
+                char **message)
+{
+        char *pomA, *pomB, *pomC;
+        int i = 0, len, tot_len = 0, nconditions, row = 0;
+        int *len_list;
+        char **list;
+
+       
+       pomC = strdup("");
+
+       while (conditions && conditions[row]) {
+
+               pomA = pomB = NULL;
+       
+               for (i=0; conditions[row][i].attr != EDG_WLL_QUERY_ATTR_UNDEF ; i++);
+               nconditions = i;
+       
+               list = (char **) malloc ((nconditions) * sizeof(*list));
+               len_list = (int *) malloc ((nconditions) * sizeof(*len_list));
+       
+               i=0;
+               while (i < nconditions) {
+                       const char *pomOp;
+                       char       *pomValue;
+       
+                       asprintf(&pomValue,"%s","");
+       
+                       switch (conditions[row][i].attr) {
+                                       /* job specific conditions */
+                                       case EDG_WLL_QUERY_ATTR_JOBID:
+                                               edg_wll_add_jobid_to_XMLBody(&pomValue, conditions[row][i].value.j, "jobId", NULL);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_TIME:
+                                               if (conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN) {
+                                                       edg_wll_add_tagged_time_t_to_XMLBody(&pomValue, conditions[row][i].value.t.tv_sec, "time", edg_wll_StatToString(conditions[row][i].attr_id.state), "state", -1);
+                                                       edg_wll_add_tagged_time_t_to_XMLBody(&pomValue, conditions[row][i].value2.t.tv_sec, "time", edg_wll_StatToString(conditions[row][i].attr_id.state), "state", -1);       
+                                               }
+                                               else 
+                                                       edg_wll_add_tagged_time_t_to_XMLBody(&pomValue, conditions[row][i].value.t.tv_sec, "time", edg_wll_StatToString(conditions[row][i].attr_id.state), "state", -1);
+                                               break;
+                               
+                                       case EDG_WLL_QUERY_ATTR_PARENT:
+                                               edg_wll_add_jobid_to_XMLBody(&pomValue, conditions[row][i].value.j, "parent_job", NULL);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_OWNER:
+                                               edg_wll_add_string_to_XMLBody(&pomValue, (conditions[row][i].value.c) ? conditions[row][i].value.c : 
+                                                                                                               "NULL", "owner", NULL);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_STATUS:
+                                               edg_wll_add_edg_wll_JobStatCode_to_XMLBody(&pomValue, conditions[row][i].value.i, "status", EDG_WLL_JOB_UNDEF);
+                                               if (conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN)
+                                                       edg_wll_add_edg_wll_JobStatCode_to_XMLBody(&pomValue, conditions[row][i].value2.i, "status", EDG_WLL_JOB_UNDEF);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_LOCATION:
+                                               edg_wll_add_string_to_XMLBody(&pomValue, conditions[row][i].value.c, "location", NULL);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_DESTINATION:
+                                               edg_wll_add_string_to_XMLBody(&pomValue, conditions[row][i].value.c, "destination", NULL);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                                               edg_wll_add_int_to_XMLBody(&pomValue, conditions[row][i].value.i, "resubmitted", -1);
+                                               if (conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN)
+                                                       edg_wll_add_int_to_XMLBody(&pomValue, conditions[row][i].value2.i, "resubmitted", -1);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_DONECODE:
+                                               edg_wll_add_int_to_XMLBody(&pomValue, conditions[row][i].value.i, "donecode", -1);
+                                               if (conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN)
+                                                       edg_wll_add_int_to_XMLBody(&pomValue, conditions[row][i].value2.i, "donecode", -1);
+                                               break;
+                                       case EDG_WLL_QUERY_ATTR_EXITCODE:
+                                               edg_wll_add_int_to_XMLBody(&pomValue, conditions[row][i].value.i, "exitcode", -1);
+                                               if (conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN)
+                                                       edg_wll_add_int_to_XMLBody(&pomValue, conditions[row][i].value2.i, "exitcode", -1);
+                                               break;
+                                       /* common conditions */
+                                       case EDG_WLL_QUERY_ATTR_USERTAG:
+                                               edg_wll_add_tagged_string_to_XMLBody(&pomValue, conditions[row][i].value.c, "usertag", conditions[row][i].attr_id.tag, "name", NULL);
+                                               break;
+                               default:
+                                       free(pomValue);
+                                       return -1;
+                       }
+                       switch (conditions[row][i].op) {
+                               case EDG_WLL_QUERY_OP_EQUAL: pomOp = "equal"; break;
+                               case EDG_WLL_QUERY_OP_LESS: pomOp = "less"; break;
+                               case EDG_WLL_QUERY_OP_GREATER: pomOp = "greater"; break;
+                               case EDG_WLL_QUERY_OP_WITHIN: pomOp = "within"; break;
+                               case EDG_WLL_QUERY_OP_UNEQUAL: pomOp = "unequal"; break;
+                               default:
+                                       return -1;
+                       }
+       
+                       len = asprintf(&list[i],"\t\t\t<%s>\n\t\t%s\t\t\t</%s>\r\n",
+                                       pomOp,pomValue,pomOp);
+                       tot_len += len;
+                       len_list[i] = len;
+       
+                       free(pomValue);
+       
+                       i++;
+               }
+       
+       
+               pomA = (char *) malloc(tot_len  * sizeof(char) +
+                       sizeof(QUERY_JOBS_OR_BEGIN) + sizeof(QUERY_JOBS_OR_END) - 1);
+       
+               memcpy(pomA, QUERY_JOBS_OR_BEGIN, sizeof(QUERY_JOBS_OR_BEGIN));
+               pomB = pomA + sizeof(QUERY_JOBS_OR_BEGIN) - 1;
+       
+               for (i=0; i < nconditions; i++) {
+                       memcpy(pomB, list[i], len_list[i] );
+                       pomB += len_list[i];
+                       free(list[i]);
+               }
+               free(list);
+               free(len_list);
+               
+               strcpy(pomB, QUERY_JOBS_OR_END);
+               asprintf(message,"%s%s", pomC, pomA);
+               free(pomA);
+               free(pomC);
+               pomC = *message;                
+               *message = NULL;
+
+               row++;
+       }
+
+
+       asprintf(message,"%s", pomC);   
+
+       free(pomC);
+
+        return 0;
+}
+
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_QueryEvents */
+int edg_wll_QueryEventsRequestToXML(
+               edg_wll_Context ctx,
+                const edg_wll_QueryRec **job_conditions,
+                const edg_wll_QueryRec **event_conditions,
+                char **message)
+{
+        char *pomA, *pomB, *pomC;
+        int i = 0, len, tot_len = 0, row = 0;
+        int nevent_conditions;
+        char **list;
+        int *len_list;
+
+       
+       edg_wll_JobQueryRecToXML(ctx, job_conditions, &pomC);
+
+       row = 0;
+       while(event_conditions && event_conditions[row]) {      
+
+               pomA = pomB = NULL;
+
+               for (i=0; event_conditions[row][i].attr != EDG_WLL_QUERY_ATTR_UNDEF ; i++);
+               nevent_conditions = i;
+       
+                list = (char **) malloc ((nevent_conditions) * sizeof(*list));
+                len_list = (int *) malloc ((nevent_conditions) * sizeof(*len_list));
+
+               i=0;
+               while (i < nevent_conditions) {
+                       const char *pomOp;
+                       char       *pomValue;
+       
+                       asprintf(&pomValue,"%s","");
+       
+                       switch (event_conditions[row][i].attr) {
+                               /* event specific conditions */
+                               case EDG_WLL_QUERY_ATTR_TIME:
+                                       if (event_conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN) {
+                                               edg_wll_add_tagged_time_t_to_XMLBody(&pomValue, event_conditions[row][i].value.t.tv_sec, "time", edg_wll_StatToString(event_conditions[row][i].attr_id.state), "state", -1);
+                                               edg_wll_add_tagged_time_t_to_XMLBody(&pomValue, event_conditions[row][i].value2.t.tv_sec, "time", edg_wll_StatToString(event_conditions[row][i].attr_id.state), "state", -1);   
+                                       }
+                                       else 
+                                               edg_wll_add_tagged_time_t_to_XMLBody(&pomValue, event_conditions[row][i].value.t.tv_sec, "time", edg_wll_StatToString(event_conditions[row][i].attr_id.state), "state", -1);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_LEVEL:
+                                       edg_wll_add_int_to_XMLBody(&pomValue, event_conditions[row][i].value.i, "level", -1);
+                                       if (event_conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN)
+                                               edg_wll_add_int_to_XMLBody(&pomValue, event_conditions[row][i].value2.i, "level", -1);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_HOST:
+                                       edg_wll_add_string_to_XMLBody(&pomValue, event_conditions[row][i].value.c, "host", NULL);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_SOURCE:
+                                       edg_wll_add_int_to_XMLBody(&pomValue, event_conditions[row][i].value.i, "source", -1);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_INSTANCE:
+                                       edg_wll_add_string_to_XMLBody(&pomValue, event_conditions[row][i].value.c, "instance", NULL);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_EVENT_TYPE:
+                                       edg_wll_add_int_to_XMLBody(&pomValue, event_conditions[row][i].value.i, "type", EDG_WLL_EVENT_UNDEF);
+                                       if (event_conditions[row][i].op == EDG_WLL_QUERY_OP_WITHIN)
+                                               edg_wll_add_int_to_XMLBody(&pomValue, event_conditions[row][i].value2.i, "type", EDG_WLL_EVENT_UNDEF);
+                                       break;
+                               /* common conditions */
+                               case EDG_WLL_QUERY_ATTR_USERTAG:
+                                       edg_wll_add_tagged_string_to_XMLBody(&pomValue, event_conditions[row][i].value.c, "usertag", event_conditions[row][i].attr_id.tag, "name", NULL);
+                                       break;
+                               default:
+                                       free(pomValue);
+                                       return -1;
+                       }
+                       switch (event_conditions[row][i].op) {
+                               case EDG_WLL_QUERY_OP_EQUAL: pomOp = "equal"; break;
+                               case EDG_WLL_QUERY_OP_LESS: pomOp = "less"; break;
+                               case EDG_WLL_QUERY_OP_GREATER: pomOp = "greater"; break;
+                               case EDG_WLL_QUERY_OP_WITHIN: pomOp = "within"; break;
+                               case EDG_WLL_QUERY_OP_UNEQUAL: pomOp = "unequal"; break;
+                               default:
+                                       return -1;
+                       }
+       
+                       len = asprintf(&list[i],"\t\t\t<%s>\n\t\t%s\t\t\t</%s>\r\n",
+                                       pomOp,pomValue,pomOp);
+       
+                       tot_len += len;
+                       len_list[i] = len;
+       
+                       free(pomValue);
+       
+                       i++;
+               }
+
+                pomA = (char *) malloc(tot_len  * sizeof(char) +
+                        sizeof(QUERY_EVENTS_OREC_BEGIN) + sizeof(QUERY_EVENTS_OREC_END) - 1);
+
+                memcpy(pomA, QUERY_EVENTS_OREC_BEGIN, sizeof(QUERY_EVENTS_OREC_BEGIN));
+                pomB = pomA + sizeof(QUERY_EVENTS_OREC_BEGIN) - 1;
+
+                for (i=0; i < nevent_conditions; i++) {
+                        memcpy(pomB, list[i], len_list[i] );
+                        pomB += len_list[i];
+                        free(list[i]);
+                }
+                free(list);
+                free(len_list);
+
+                strcpy(pomB, QUERY_EVENTS_OREC_END);
+                asprintf(message,"%s%s", pomC, pomA);
+                free(pomA);
+                free(pomC);
+                pomC = *message;
+                *message = NULL;
+
+               row++;
+       }       
+       
+       asprintf(message,"%s softLimit=\"%d\" queryRes=\"%d\">\r\n\t<and>\r\n%s%s",
+               QUERY_EVENTS_REQUEST_BEGIN, 
+               ctx->p_query_events_limit, ctx->p_query_results,
+               pomC, QUERY_EVENTS_REQUEST_END);        
+
+       free(pomC);
+
+       return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_QueryJobs */
+int edg_wll_QueryJobsRequestToXML(
+               edg_wll_Context ctx,
+               const edg_wll_QueryRec **conditions,
+                int flags,
+                char **message)
+{
+        char *pomC, *cflags;
+
+       
+       edg_wll_JobQueryRecToXML(ctx, conditions, &pomC);       
+
+       asprintf(message,
+               "%s softLimit=\"%d\" queryRes=\"%d\">\r\n\t<flags>%s</flags>\r\n\t<and>\r\n%s%s",
+               QUERY_JOBS_REQUEST_BEGIN,
+               ctx->p_query_jobs_limit, ctx->p_query_results,
+               cflags = edg_wll_stat_flags_to_string(flags), pomC, QUERY_JOBS_REQUEST_END);    
+
+       free(cflags);
+       free(pomC);
+
+        return 0;
+}
+
+
+/* construct Message-Body of Request-Line for edg_wll_Purge */
+int edg_wll_PurgeRequestToXML(
+                edg_wll_Context ctx,
+               const edg_wll_PurgeRequest *request,
+                char **message)
+{
+        char *pomA, *pomB, *pomC;
+
+
+       if (!request) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       if (request->jobs) edg_wll_add_strlist_to_XMLBody(&pomA, request->jobs, "jobs",
+                                                               "jobId", "\t", NULL);
+
+       /* print timeout for all status codes */
+       pomB = strdup("");
+       edg_wll_add_time_t_list_to_XMLBody(&pomB, request->timeout, "timeout", edg_wll_StatToString, "\t",
+                                               0, EDG_WLL_NUMBER_OF_STATCODES);
+
+       trio_asprintf(&pomC,"%s%s%s\t<flags>%|Xs</flags>\r\n%s",
+                       PURGE_REQUEST_BEGIN,pomA,pomB,edg_wll_purge_flags_to_string(request->flags),
+                       PURGE_REQUEST_END);
+
+       free(pomA);
+       free(pomB);
+
+
+        *message = pomC;
+
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Dump */
+int edg_wll_DumpRequestToXML(
+                edg_wll_Context ctx,
+               const edg_wll_DumpRequest *request,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!request) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       if (request->from < 0)
+               edg_wll_add_string_to_XMLBody(&pomA, 
+                       edg_wll_DumpConstToString(request->from), "from", NULL);
+       else
+       edg_wll_add_time_t_to_XMLBody(&pomA, request->from, "from", 0);
+       if (request->to < 0)
+               edg_wll_add_string_to_XMLBody(&pomA,
+                       edg_wll_DumpConstToString(request->to), "to", NULL);
+       else
+       edg_wll_add_time_t_to_XMLBody(&pomA, request->to, "to", 0);
+
+       trio_asprintf(&pomB,"%s%s%s",
+                       DUMP_REQUEST_BEGIN,pomA,DUMP_REQUEST_END);
+
+       free(pomA);
+
+
+        *message = pomB;
+
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Load */
+int edg_wll_LoadRequestToXML(
+                edg_wll_Context ctx,
+               const edg_wll_LoadRequest *request,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!request) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       edg_wll_add_string_to_XMLBody(&pomA, request->server_file, "server_file", 0);
+
+       trio_asprintf(&pomB,"%s%s%s",
+                       LOAD_REQUEST_BEGIN,pomA,LOAD_REQUEST_END);
+
+       free(pomA);
+
+
+        *message = pomB;
+
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_IndexedAttrs */
+int edg_wll_IndexedAttrsRequestToXML(
+                edg_wll_Context ctx,
+                char **message)
+{
+       char *pomB;
+
+
+       trio_asprintf(&pomB,"%s%s",
+                       INDEXED_ATTRS_REQUEST_BEGIN,INDEXED_ATTRS_REQUEST_END);
+
+        *message = pomB;
+
+        return 0;
+}
+
+
+/* construct Message-Body of Request-Line for edg_wll_Notif* functions */
+int edg_wll_NotifRequestToXML(
+                edg_wll_Context ctx, 
+               const char *function,
+               const edg_wll_NotifId notifId,
+               const char *address,
+               edg_wll_NotifChangeOp op,
+               edg_wll_QueryRec const * const *conditions,
+                char **message)
+{
+       char *pomA=NULL, *pomB=NULL, *pomC=NULL;
+
+
+       pomA = strdup("");
+       edg_wll_add_string_to_XMLBody(&pomA, edg_wll_NotifIdUnparse(notifId), "notifId", NULL);
+       edg_wll_add_string_to_XMLBody(&pomA, address, "clientAddress", NULL);
+       edg_wll_add_int_to_XMLBody(&pomA, op, "notifChangeOp", EDG_WLL_NOTIF_NOOP);
+       if (conditions && conditions[0] && conditions[0][0].attr != EDG_WLL_QUERY_ATTR_UNDEF)
+               edg_wll_JobQueryRecToXML(ctx, conditions, &pomB);
+               
+
+       if (pomB)
+               trio_asprintf(&pomC,"%s function=\"%s\">\r\n%s\t<and>\r\n%s\t</and>\r\n%s",
+                       NOTIF_REQUEST_BEGIN,function,pomA,pomB,NOTIF_REQUEST_END);
+       else
+               trio_asprintf(&pomC,"%s function=\"%s\">\r\n%s%s",
+                       NOTIF_REQUEST_BEGIN,function,pomA,NOTIF_REQUEST_END);
+
+
+       free(pomA);
+       free(pomB);
+        *message = pomC;
+
+        return 0;
+}
diff --git a/org.glite.lb.server/Makefile b/org.glite.lb.server/Makefile
new file mode 100644 (file)
index 0000000..b3a55bd
--- /dev/null
@@ -0,0 +1,79 @@
+include Makefile.inc
+
+YACC=bison -y
+
+VPATH:=${src} 
+AT3:=perl -I${lbconfig} ${lbproject}/at3
+SUFFIXES = .T 
+
+DEBUG:=-g -O0 
+CFLAGS:=${DEBUG} -I${stageinc} -I${src} -I. \
+       -I${repository}/${voms}/include \
+       -I${repository}/${gacl}/include \
+       -I${repository}/${expat}/include \
+       -I${repository}/${mysql}/include \
+       -I/usr/include/libxml2 \
+       -I${repository}/${globus}/include/${globusflavour} \
+       -I${repository}/${globus}/include/${globusflavour}/openssl
+
+LINK:=libtool --mode=link ${CC} ${LDFLAGS} 
+INSTALL:=libtool --mode=install install
+
+# assisst & gss due to VOMS only
+
+GLOBUS_LIBS:= -L${repository}/${globus}/lib \
+       -lglobus_gss_assist_${globusflavour} \
+       -lglobus_gssapi_gsi_${globusflavour} \
+       -lglobus_common_${globusflavour} \
+       -lssl_${globusflavour}
+
+
+# XXX: our vomsc.la depends on badly installed expat
+
+EXT_LIBS:= -L${repository}/${ares}/lib -lares \
+       -L${repository}/${mysql}/lib -lmysqlclient \
+       -L${repository}/${gacl}/lib -lgacl \
+       ${repository}/${voms}/lib/libvomsc.a \
+       -L${repository}/${expat}/lib -lexpat \
+       ${GLOBUS_LIBS}
+
+COMMON_LIB:= -L${stagelib} -lglite_lb_common
+
+HELPERS:= -L${stagelib} -lglite_wms_tls_ssl_helpers
+
+
+SERVER_OBJS:= bkserverd.o get_events.o index.o jobstat.o jobstat_supp.o \
+       write2rgma.o lbs_db.o lb_html.o lb_http.o lb_proto.o lb_xml_parse.o \
+       lock.o openserver.o query.o userjobs.o db_store.o request.o store.o \
+       stored_master.o srv_purge.o server_state.o dump.o lb_authz.o load.o \
+       notification.o il_notification.o notif_match.o
+
+INDEX_OBJS:= index.o index_parse.o jobstat_supp.o lbs_db.o openserver.o \
+       jobstat.o query.o lock.o get_events.o write2rgma.o index_lex.o \
+       lb_authz.o store.o bkindex.o
+
+default: stage
+
+compile: glite_lb_bkserverd glite_lb_bkindex
+
+glite_lb_bkserverd: ${SERVER_OBJS}
+       ${LINK} -o $@ ${SERVER_OBJS} ${COMMON_LIB} ${HELPERS} ${EXT_LIBS}
+
+glite_lb_bkindex: ${INDEX_OBJS}
+       ${LINK} -o $@ ${INDEX_OBJS} ${COMMON_LIB} ${HELPERS} ${EXT_LIBS}
+
+all stage export: compile
+       ${INSTALL} -m 755 glite_lb_bkserverd glite_lb_bkindex ${stagebin}
+
+
+%.c: %.c.T
+       rm -f $@
+       ${AT3} $< >$@ || rm -f $@
+       chmod -w $@ >/dev/null
+
+%.o: %.y
+       ${YACC} -d ${YFLAGS} $<
+       mv y.tab.c $*.c
+       mv y.tab.h $*.h
+       ${CC} -c ${CFLAGS} $*.c
+       rm $*.c
diff --git a/org.glite.lb.server/build.xml b/org.glite.lb.server/build.xml
new file mode 100755 (executable)
index 0000000..4aa4105
--- /dev/null
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="lb" default="dist">
+       
+       <import file="../org.glite/project/baseline.properties.xml" />
+       <import file="./project/properties.xml"/>
+       <import file="${subsystem.properties.file}"/>
+       <import file="${global.properties.file}" />
+
+       <property file="${user.dependencies.file}"/>
+       <property file="${component.dependencies.file}" />
+       <property file="${subsystem.dependencies.file}" />
+       <property file="${global.dependencies.file}"/>
+       
+       <import file="${subsystem.taskdefs.file}" />
+       <import file="${global.taskdefs.file}" />
+
+       <import file="${global.targets-external-dependencies.file}"/>   
+       <import file="${global.targets-make.file}" />
+               
+       <property file="${module.version.file}"/>
+
+       <target name="localinit"
+               description="Module specific initialization tasks">
+               <antcall target="lbmakefiles"/>
+       </target>
+
+       <target name="localcompile"
+               description="Module specific compile tasks">
+       </target>
+       
+       <target name="localclean"
+               description="Module specific cleaning tasks">
+       </target>
+       
+</project>             
diff --git a/org.glite.lb.server/project/properties.xml b/org.glite.lb.server/project/properties.xml
new file mode 100755 (executable)
index 0000000..0ca2172
--- /dev/null
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB server component common properties">
+
+       <property file="build.properties" />    
+       <property name="subsystem.name" value="${lb.subsystem.name}"/>
+       <property name="subsystem.prefix" value="${lb.subsystem.prefix}"/>
+       <property name="component.prefix" value="server" />
+
+       <import file="${component.general.properties.file}" />
+
+</project>
diff --git a/org.glite.lb.server/src/bkindex.c b/org.glite.lb.server/src/bkindex.c
new file mode 100644 (file)
index 0000000..3c230ee
--- /dev/null
@@ -0,0 +1,305 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <sysexits.h>
+#include <assert.h>
+
+#include "glite/wms/jobid/strmd5.h"
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "index.h"
+#include "lbs_db.h"
+#include "jobstat.h"
+
+static struct option opts[] = {
+       { "mysql",1,NULL,'m' },
+       { "remove",0,NULL,'R' },
+       { "really",0,NULL,'r' },
+       { "dump",0,NULL,'d' },
+       { "verbose",0,NULL,'v' },
+       { NULL, 0, NULL, 0 }
+};
+
+static void usage(const char *);
+static void do_exit(edg_wll_Context,int);
+static char *col_list(const edg_wll_QueryRec *);
+static char *db_col_type(const edg_wll_QueryRec *);
+
+/* XXX: don't bother with malloc() of arrays that are tiny in real life */
+#define CI_MAX 200
+
+int debug;
+
+int main(int argc,char **argv)
+{
+       int     opt;
+       char    *dbstring = getenv("LBDB");
+       char    *fname = "-";
+       int     dump = 0, really = 0, verbose = 0, remove_all = 0;
+       edg_wll_Context ctx;
+       edg_wll_QueryRec        **old_indices,**new_indices;
+       edg_wll_QueryRec        *new_columns[CI_MAX],*old_columns[CI_MAX];
+       int     add_columns[CI_MAX],drop_columns[CI_MAX];
+       int     add_indices[CI_MAX],drop_indices[CI_MAX];
+       edg_wll_IColumnRec *added_icols;
+       int     nadd_icols;
+       char    **index_names;
+       int     i,j,k;
+       int     nnew,nold,nadd,ndrop;
+       char    *stmt;
+
+       for (i=0; i<CI_MAX; i++) add_indices[i] = drop_indices[i] = -1;
+       memset(new_columns,0,sizeof new_columns);
+       memset(old_columns,0,sizeof old_columns);
+       memset(add_columns,0,sizeof add_columns);
+       memset(drop_columns,0,sizeof drop_columns);
+
+       while ((opt = getopt_long(argc,argv,"m:rRdv",opts,NULL)) != EOF) switch (opt) {
+               case 'm': dbstring = optarg; break;
+               case 'r': really = 1; break;
+               case 'R': remove_all = 1; break;
+               case 'd': dump = 1; break;
+               case 'v': verbose++; break;
+               case '?': usage(argv[0]); exit(EX_USAGE);
+       }
+
+       if (really && dump) { usage(argv[0]); exit(EX_USAGE); }
+       if (really && remove_all) { usage(argv[0]); exit(EX_USAGE); }
+       if (optind < argc) {
+               if (dump || remove_all) { usage(argv[0]); exit(EX_USAGE); }
+               else fname = argv[optind];
+       }
+
+       edg_wll_InitContext(&ctx);
+       if (edg_wll_Open(ctx,dbstring)) do_exit(ctx,EX_UNAVAILABLE);
+       if (edg_wll_DBCheckVersion(ctx)) do_exit(ctx,EX_SOFTWARE);
+       if (edg_wll_QueryJobIndices(ctx,&old_indices,&index_names)) do_exit(ctx,EX_SOFTWARE);
+
+       if (dump) {
+               if (edg_wll_DumpIndexConfig(ctx,fname,old_indices)) do_exit(ctx,EX_SOFTWARE);
+               else if ( !remove_all ) return 0;
+       }
+
+       if (remove_all) {
+               if (verbose) printf("Dropping all indices");
+               for (i=0; index_names && index_names[i]; i++) {
+                       asprintf(&stmt,"alter table states drop index `%s`",index_names[i]);
+                       if (verbose) putchar('.');
+                       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) do_exit(ctx,EX_SOFTWARE);
+                       free(stmt);
+               }
+               if (verbose) puts(" done");
+               if (verbose) printf("Dropping index columns");
+               for (i=0; old_indices && old_indices[i]; i++) {
+                       char *cname = edg_wll_QueryRecToColumn(old_indices[i]);
+                       asprintf(&stmt,"alter table states drop column `%s`",cname);
+                       if (verbose) putchar('.');
+                       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) do_exit(ctx,EX_SOFTWARE);
+                       free(stmt);
+                       free(cname);
+               }
+               if (verbose) puts(" done");
+               return 0;
+       }
+
+       if (edg_wll_ParseIndexConfig(ctx,fname,&new_indices))
+               do_exit(ctx,edg_wll_Error(ctx,NULL,NULL) == EINVAL ? EX_DATAERR : EX_IOERR);
+
+       /* FIXME: check duplicate columns in indices */
+
+/* indices to add & drop */
+       nadd = ndrop = 0;
+       if (old_indices && new_indices) {
+               for (i=0; old_indices[i]; i++) {
+                       for (j=0; new_indices[j]; j++) {
+                               for (k=0; old_indices[i][k].attr &&
+                                       !edg_wll_CmpColumn(&old_indices[i][k],&new_indices[j][k]);
+                                       k++);
+                               if (!old_indices[i][k].attr && !new_indices[j][k].attr) break;
+                       }
+                       if (!new_indices[j]) drop_indices[ndrop++] = i;
+               }
+                                       
+               for (i=0; new_indices[i]; i++) {
+                       for (j=0; old_indices[j]; j++) {
+                               for (k=0; new_indices[i][k].attr &&
+                                       !edg_wll_CmpColumn(&new_indices[i][k],&old_indices[j][k]);
+                                       k++);
+                               if (!new_indices[i][k].attr && !old_indices[j][k].attr) break;
+                       }
+                       if (!old_indices[j]) add_indices[nadd++] = i;
+               }
+       }
+       else if (new_indices) for (i=0; new_indices[i]; i++) add_indices[i] = i;
+       else if (old_indices) for (i=0; old_indices[i]; i++) drop_indices[i] = i;
+
+/* old and new column sets */
+       nold = nnew = 0;
+       if (old_indices) for (i=0; old_indices[i]; i++) 
+               for (j=0; old_indices[i][j].attr; j++) {
+                       for (k=0; k<nold && edg_wll_CmpColumn(old_columns[k],&old_indices[i][j]); k++);
+                       if (k == nold) {
+                               assert(nold < CI_MAX);
+                               old_columns[nold++] = &old_indices[i][j];
+                       }
+               }
+
+       if (new_indices) for (i=0; new_indices[i]; i++) 
+               for (j=0; new_indices[i][j].attr; j++) {
+                       for (k=0; k<nnew && edg_wll_CmpColumn(new_columns[k],&new_indices[i][j]); k++);
+                       if (k == nnew) {
+                               assert(nnew < CI_MAX);
+                               new_columns[nnew++] = &new_indices[i][j];
+                       }
+               }
+
+/* go! */
+       if (!really) puts("\n** Dry run, no actual actions performed **\n");
+
+       if (verbose) puts("Dropping indices ...");
+       for (i=0; drop_indices[i] >= 0; i++) {
+               if (verbose) { 
+                       char    *n = col_list(old_indices[drop_indices[i]]);
+                       printf("\t%s(%s) ... ",index_names[drop_indices[i]],n); fflush(stdout);
+                       free(n);
+               }
+               if (really) {
+                       asprintf(&stmt,"alter table states drop index `%s`",index_names[drop_indices[i]]);
+                       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) do_exit(ctx,EX_SOFTWARE);
+                       free(stmt);
+               }
+               if (verbose) puts(really ? "done" : "");
+       }
+
+       if (verbose) puts("Dropping columns ...");
+       for (i=0; i<nold; i++) {
+               for (j=0; j<nnew && edg_wll_CmpColumn(old_columns[i],new_columns[j]); j++);
+               if (j == nnew) {
+                       char    *cname = edg_wll_QueryRecToColumn(old_columns[i]);
+                       if (verbose) printf("\t%s\n",cname); 
+                       if (really) {
+                               asprintf(&stmt,"alter table states drop column `%s`",cname);
+                               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) do_exit(ctx,EX_SOFTWARE);
+                               free(stmt);
+                       }
+                       free(cname);
+               }
+       }
+
+       added_icols = calloc(nnew+1, sizeof(edg_wll_IColumnRec));
+       nadd_icols = 0;
+       if (verbose) puts("Adding columns ...");
+       for (i=0; i<nnew; i++) {
+               for (j=0; j<nold && edg_wll_CmpColumn(new_columns[i],old_columns[j]); j++);
+               if (j == nold) {
+                       char    *cname = edg_wll_QueryRecToColumn(new_columns[i]);
+                       if (verbose) printf("\t%s\n",cname); 
+                       if (really) {
+                               char    *ctype = db_col_type(new_columns[i]);
+                               asprintf(&stmt,"alter table states add `%s` %s",cname,ctype);
+                               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) do_exit(ctx,EX_SOFTWARE);
+                               free(stmt);
+                       }
+                       memcpy(&added_icols[nadd_icols].qrec, new_columns[i], sizeof(edg_wll_QueryRec));
+                       added_icols[nadd_icols].colname = strdup(cname);
+                       if (new_columns[i]->attr == EDG_WLL_QUERY_ATTR_USERTAG)
+                               added_icols[nadd_icols].qrec.attr_id.tag =
+                                       strdup(new_columns[i]->attr_id.tag);
+                       nadd_icols++;
+                       free(cname);
+               }
+       }
+
+       if (nadd_icols) {
+               added_icols[nadd_icols].qrec.attr = EDG_WLL_QUERY_ATTR_UNDEF;
+               added_icols[nadd_icols].colname = NULL;
+               if (verbose) puts("Refreshing data ...");
+               if (really && edg_wll_RefreshIColumns(ctx, (void*) added_icols)) do_exit(ctx,EX_SOFTWARE);
+       }
+       else if (verbose) puts("No data refresh required");
+
+       for (nadd_icols--; nadd_icols >= 0; nadd_icols--)
+               edg_wll_FreeIColumnRec(&added_icols[nadd_icols]);
+       free(added_icols);
+
+       if (verbose) puts("Creating indices ...");
+       for (i=0; add_indices[i] >= 0; i++) {
+               char    *l = col_list(new_indices[add_indices[i]]);
+               char    *n = str2md5base64(l);
+               if (verbose) { printf("\t%s(%s) ... ",n,l); fflush(stdout); }
+               if (really) {
+                       asprintf(&stmt,"create index `%s` on states(%s)",n,l);
+                       free(n); free(l);
+                       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) do_exit(ctx,EX_SOFTWARE);
+                       free(stmt);
+               }
+               if (verbose) puts(really ? "done" : "");
+       }
+
+       return 0;
+}
+
+static char *col_list(const edg_wll_QueryRec *ind)
+{
+       char    *ret,*aux,size[50] = "";
+       int     j;
+
+       aux = edg_wll_QueryRecToColumn(ind);
+       if (ind->value.i) sprintf(size,"(%d)",ind->value.i);
+       asprintf(&ret,"`%s`%s",aux,size);
+       free(aux);
+
+       for (j=1; ind[j].attr; j++) {
+               char    *n = edg_wll_QueryRecToColumn(ind+j),size[50] = "";
+               if (ind[j].value.i) sprintf(size,"(%d)",ind[j].value.i);
+               aux = ret;
+               asprintf(&ret,"%s,`%s`%s",aux,n,size);
+               free(n); free(aux);
+       }
+       return ret;
+}
+
+static char *db_col_type(const edg_wll_QueryRec *r)
+{
+       switch (r->attr) {
+               case EDG_WLL_QUERY_ATTR_OWNER:
+               case EDG_WLL_QUERY_ATTR_LOCATION:
+               case EDG_WLL_QUERY_ATTR_DESTINATION:
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+               case EDG_WLL_QUERY_ATTR_HOST:
+               case EDG_WLL_QUERY_ATTR_CHKPT_TAG:
+                       return "char(250) binary null";
+
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       return "datetime null";
+               default:
+                       return NULL;
+       }
+}
+
+static void do_exit(edg_wll_Context ctx,int code)
+{
+       char    *et,*ed;
+
+       edg_wll_Error(ctx,&et,&ed);
+       fprintf(stderr,"edg-bkindex: %s (%s)\n",et,ed);
+       exit(code);
+}
+
+static void usage(const char *me)
+{
+       fprintf(stderr,"usage: %s <options> [file]\n"
+                       "       -m,--mysql <dbstring>   use non-default database connection\n"
+                       "       -r,--really             really perform reindexing\n"
+                       "       -R,--remove             remove all indexes from server\n"
+                       "       -d,--dump               dump current setup\n"
+                       "       -v,--verbose            increase verbosity (2 levels)\n"
+                       "\n     -r and -d are mutually exlusive\n"
+                       "\n     -r and -R are mutually exlusive\n"
+                       "       [file] is applicable only without -d and -R\n",
+                       me);
+}
diff --git a/org.glite.lb.server/src/bkserverd.c b/org.glite.lb.server/src/bkserverd.c
new file mode 100644 (file)
index 0000000..1d22296
--- /dev/null
@@ -0,0 +1,1308 @@
+#ident "$Header$"
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <linux/limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <signal.h>
+#include <errno.h>
+#include <netdb.h>
+#include <limits.h>
+#include <assert.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <ares.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+
+#include <globus_common.h>
+
+#include "glite/wms/tls/ssl_helpers/ssl_inits.h"
+#include "glite/lb/consumer.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/context.h"
+#include "glite/lb/mini_http.h"
+#include "lb_http.h"
+#include "lb_proto.h"
+#include "index.h"
+#include "lbs_db.h"
+#include "lb_authz.h"
+#include "il_notification.h"
+
+extern int edg_wll_StoreProto(edg_wll_Context ctx);
+extern edg_wll_ErrorCode edg_wll_Open(edg_wll_Context ctx, char *cs);
+edg_wll_ErrorCode edg_wll_Close(edg_wll_Context);
+
+#define max(x,y) ((x)>(y)?(x):(y))
+
+#define CON_QUEUE              20      /* accept() */
+#define SLAVE_OVERLOAD         10      /* queue items per slave */
+#define CLNT_TIMEOUT           10      /* keep idle connection that many seconds */
+#define TOTAL_CLNT_TIMEOUT     60      /* one client may ask one slave multiple times */
+                                       /* but only limited time to avoid DoS attacks */
+#define CLNT_REJECT_TIMEOUT    100000  /* time limit for client rejection in !usec! */
+#define DNS_TIMEOUT            5       /* how long wait for DNS lookup */
+#define SLAVE_CONNS_MAX                500     /* commit suicide after that many connections */
+#define MASTER_TIMEOUT         30      /* maximal time of one-round of master network communication */
+#define SLAVE_TIMEOUT          30      /* maximal time of one-round of slave network communication */
+#define SLAVE_CHECK_SIGNALS    2       /* how often to check signals while waiting for recv_mesg */
+#define WATCH_TIMEOUT          1800    /* wake up to check updated credentials */
+
+#ifndef EDG_PURGE_STORAGE
+#define EDG_PURGE_STORAGE      "/tmp/purge"
+#endif
+
+#ifndef EDG_DUMP_STORAGE
+#define EDG_DUMP_STORAGE       "/tmp/dump"
+#endif
+
+/* file to store pid and generate semaphores key */
+#ifndef EDG_BKSERVERD_PIDFILE
+#define EDG_BKSERVERD_PIDFILE  "/var/run/edg-bkserverd.pid"
+#endif
+
+#ifndef dprintf
+#define dprintf(x) { if (debug) printf x; }
+#endif
+
+       static const int one = 1;
+
+int debug  = 0;
+int rgma_export = 0;
+static int noAuth = 0;
+static int noIndex = 0;
+static int strict_locking = 0;
+static int hardJobsLimit = 0;
+static int hardEventsLimit = 0;
+static int hardRespSizeLimit = 0;
+static char    *dbstring = NULL,*fake_host = NULL;
+static int fake_port = 0;
+
+static char    ** super_users = NULL;
+char   *cadir = NULL, *vomsdir = NULL;
+
+
+static int     slaves = 10, semaphores = -1, semset;
+static char    *purgeStorage = EDG_PURGE_STORAGE;
+static char    *dumpStorage = EDG_DUMP_STORAGE;
+static time_t  purge_timeout[EDG_WLL_NUMBER_OF_STATCODES];
+static time_t  notif_duration = 60*60*24*7;
+static edg_wll_QueryRec        **job_index;
+static edg_wll_IColumnRec *job_index_cols;
+
+static volatile int die = 0, child_died = 0;
+
+static int dispatchit(int,int,int);
+static int do_sendmsg(int,int,unsigned long,int);
+static int do_recvmsg(int,int *,unsigned long *,int *);
+
+static void wait_for_open(edg_wll_Context,const char *);
+
+static void catchsig(int sig)
+{
+       die = sig;
+}
+
+static void catch_chld(int sig)
+{
+       child_died = 1;
+}
+
+static int slave(void *,int);
+
+static struct option opts[] = {
+       {"cert",        1, NULL,        'c'},
+       {"key",         1, NULL,        'k'},
+       {"CAdir",       1, NULL,        'C'},
+       {"VOMSdir",     1, NULL,        'V'},
+       {"port",        1, NULL,        'p'},
+       {"address",     1, NULL,        'a'},
+       {"debug",       0, NULL,        'd'},
+       {"rgmaexport",  0, NULL,        'r'},
+       {"mysql",       1, NULL,        'm'},
+       {"noauth",      0, NULL,        'n'},
+       {"slaves",      1, NULL,        's'},
+       {"semaphores",  1, NULL,        'l'},
+       {"pidfile",     1, NULL,        'i'},
+       {"purge-prefix",        1, NULL,        'S'},
+       {"dump-prefix", 1, NULL,        'D'},
+       {"super-user",  1, NULL,        'R'},
+       {"super-users-file",    1, NULL,'F'},
+       {"no-index",    1, NULL,        'x'},
+       {"strict-locking",0, NULL,      'P'},
+       {"limits",      1, NULL,        'L'},
+       {"notif-dur",   1, NULL,        'N'},
+       {"notif-il-sock",       1, NULL,        'X'},
+       {NULL,0,NULL,0}
+};
+
+static void usage(char *me) 
+{
+       fprintf(stderr,"usage: %s [option]\n"
+               "\t-a, --address\t use this server address (may be faked for debugging)\n"
+               "\t-k, --key\t private key file\n"
+               "\t-c, --cert\t certificate file\n"
+               "\t-C, --CAdir\t trusted certificates directory\n"
+               "\t-V, --VOMSdir\t trusted VOMS servers certificates directory\n"
+               "\t-p, --port\t port to listen\n"
+               "\t-m, --mysql\t database connect string\n"
+               "\t-d, --debug\t don't run as daemon, additional diagnostics\n"
+               "\t-r, --rgmaexport write state info to RGMA interface\n"
+               "\t-n, --noauth\t don't check user identity with result owner\n"
+               "\t-s, --slaves\t number of slave servers to fork\n"
+               "\t-l, --semaphores number of semaphores (job locks) to use\n"
+               "\t-i, --pidfile\t file to store master pid\n"
+               "\t-L, --limits\t query limits numbers in format \"events_limit:jobs_limit:size_limit\"\n"
+               "\t-N, --notif-dur\t Maximal duration of notification registrations in hours\n"
+               "\t-S, --purge-prefix\t purge files full-path prefix\n"
+               "\t-D, --dump-prefix\t dump files full-path prefix\n"
+               "\t--super-user\t user allowed to bypass authorization and indexing\n"
+               "\t--super-users-file\t the same but read the subjects from a file\n"
+               "\t--no-index=1\t don't enforce indices for superusers\n"
+               "\t          =2\t don't enforce indices at all\n"
+               "\t--strict-locking=1\t lock jobs also on storing events (may be slow)\n"
+               "\t--notif-il-sock\t socket to send notifications\n"
+       ,me);
+}
+
+
+static int decrement_timeout(struct timeval *timeout, struct timeval before, struct timeval after)
+{
+        (*timeout).tv_sec = (*timeout).tv_sec - (after.tv_sec - before.tv_sec);
+        (*timeout).tv_usec = (*timeout).tv_usec - (after.tv_usec - before.tv_usec);
+        while ( (*timeout).tv_usec < 0) {
+                (*timeout).tv_sec--;
+                (*timeout).tv_usec += 1000000;
+        }
+        if ( ((*timeout).tv_sec < 0) || (((*timeout).tv_sec == 0) && ((*timeout).tv_usec == 0)) ) return(1);
+        else return(0);
+}
+
+static int check_timeout(struct timeval *timeout, struct timeval before, struct timeval after)
+{
+       return (timeout->tv_usec <= after.tv_usec - before.tv_usec) ? 
+                       (timeout->tv_sec <= after.tv_sec - before.tv_sec) :
+                       (timeout->tv_sec < after.tv_sec - before.tv_sec);
+}
+
+static void clnt_reject(void *mycred, int conn)
+{
+       int     flags = fcntl(conn, F_GETFL, 0);
+
+       if (fcntl(conn, F_SETFL, flags | O_NONBLOCK) < 0) 
+               return;
+
+       edg_wll_ssl_reject(mycred, conn);
+       return;
+}
+
+
+#define MSG_BUFSIZ     15
+
+/* send socket sock through socket to_sock */
+static int do_sendmsg(int to_sock, int sock, unsigned long clnt_dispatched,int store) {
+
+        struct msghdr msg = {0};
+        struct cmsghdr *cmsg;
+        int myfds;     /* Contains the file descriptors to pass. */
+        char buf[CMSG_SPACE(sizeof myfds)];  /* ancillary data buffer */
+        int *fdptr;
+        struct iovec sendiov;
+        char sendbuf[MSG_BUFSIZ];              /* to store unsigned int + \0 */
+
+
+        snprintf(sendbuf,sizeof(sendbuf),"%c %lu",store?'S':'Q',clnt_dispatched);
+
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &sendiov;
+        msg.msg_iovlen = 1;
+        sendiov.iov_base = sendbuf;
+        sendiov.iov_len = sizeof(sendbuf);
+
+        msg.msg_control = buf;
+        msg.msg_controllen = sizeof buf;
+
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        fdptr = (int *)CMSG_DATA(cmsg);
+        *fdptr = sock;
+
+        msg.msg_controllen = cmsg->cmsg_len;
+        /* send fd to server-slave to do rest of communication */
+       if (sendmsg(to_sock, &msg, 0) < 0)  
+               return 1;
+        
+        return 0;
+}
+
+
+/* receive socket sock through socket from_sock */
+static int do_recvmsg(int from_sock, int *sock, unsigned long *clnt_accepted,int *store) {
+
+        struct msghdr msg = {0};
+        struct cmsghdr *cmsg;
+        int myfds; /* Contains the file descriptors to pass. */
+        char buf[CMSG_SPACE(sizeof(myfds))];  /* ancillary data buffer */
+        struct iovec recviov;
+        char recvbuf[MSG_BUFSIZ],op;
+
+
+        msg.msg_name = NULL;
+        msg.msg_namelen = 0;
+        msg.msg_iov = &recviov;
+        msg.msg_iovlen = 1;
+        recviov.iov_base = recvbuf;
+        recviov.iov_len = sizeof(recvbuf);
+
+        msg.msg_control = buf;
+        msg.msg_controllen = sizeof buf;
+
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+        msg.msg_controllen = cmsg->cmsg_len;
+
+        if (recvmsg(from_sock, &msg, 0) < 0) 
+               return 1;
+        
+        *sock = *((int *)CMSG_DATA(cmsg));
+       sscanf(recvbuf,"%c %lu",&op,clnt_accepted);
+       *store = op == 'S';
+
+        return 0;
+}
+
+
+struct asyn_result {
+       struct hostent *ent;
+       int             err;
+};
+
+/* ares callback handler for ares_gethostbyaddr()       */
+static void callback_handler(void *arg, int status, struct hostent *h) {
+       struct asyn_result *arp = (struct asyn_result *) arg;
+
+       switch (status) {
+          case ARES_SUCCESS:
+               if (h && h->h_name) {
+                       arp->ent->h_name = strdup(h->h_name);           
+                       if (arp->ent->h_name == NULL) {
+                               arp->err = NETDB_INTERNAL;
+                       } else {
+                               arp->err = NETDB_SUCCESS;
+                       }
+               } else {
+                       arp->err = NO_DATA;
+               }
+               break;
+           case ARES_EBADNAME:
+           case ARES_ENOTFOUND:
+               arp->err = HOST_NOT_FOUND;
+               break;
+           case ARES_ENOTIMP:
+               arp->err = NO_RECOVERY;
+               break;
+           case ARES_ENOMEM:
+           case ARES_EDESTRUCTION:
+           default:
+               arp->err = NETDB_INTERNAL;
+               break;
+       }
+}
+
+static void free_hostent(struct hostent *h){
+       int i;
+
+       if (h) {
+               if (h->h_name) free(h->h_name);
+               if (h->h_aliases) {
+                       for (i=0; h->h_aliases[i]; i++) free(h->h_aliases[i]);
+                       free(h->h_aliases);
+               }
+               if (h->h_addr_list) {
+                       for (i=0; h->h_addr_list[i]; i++) free(h->h_addr_list[i]);
+                       free(h->h_addr_list);
+               }
+               free(h);
+       }
+}
+
+static int asyn_gethostbyaddr(char **name, const char *addr,int len, int type, struct timeval *timeout) {
+       struct asyn_result ar;
+       ares_channel channel;
+       int nfds;
+       fd_set readers, writers;
+       struct timeval tv, *tvp;
+       struct timeval start_time,check_time;
+
+
+/* start timer */
+        gettimeofday(&start_time,0);
+
+/* ares init */
+        if ( ares_init(&channel) != ARES_SUCCESS ) return(NETDB_INTERNAL);
+       ar.ent = (struct hostent *) malloc (sizeof(*ar.ent));
+       memset((void *) ar.ent, 0, sizeof(*ar.ent));
+
+/* query DNS server asynchronously */
+       ares_gethostbyaddr(channel, addr, len, type, callback_handler, (void *) &ar);
+
+/* wait for result */
+        while (1) {
+                FD_ZERO(&readers);
+                FD_ZERO(&writers);
+                nfds = ares_fds(channel, &readers, &writers);
+                if (nfds == 0)
+                        break;
+
+                gettimeofday(&check_time,0);
+               if (decrement_timeout(timeout, start_time, check_time)) {
+                       ares_destroy(channel);
+                       free_hostent(ar.ent);
+                       return(TRY_AGAIN);
+               }
+               start_time = check_time;
+
+                tvp = ares_timeout(channel, timeout, &tv);
+
+                switch ( select(nfds, &readers, &writers, NULL, tvp) ) {
+                       case -1: if (errno != EINTR) {
+                                       ares_destroy(channel);
+                                       free_hostent(ar.ent);
+                                       return NETDB_INTERNAL;
+                                } else
+                                       continue;
+                       case 0: 
+                               FD_ZERO(&readers);
+                               FD_ZERO(&writers);
+                               /* fallthrough */
+                        default : ares_process(channel, &readers, &writers);
+                }
+
+        }
+
+       
+       ares_destroy(channel);
+               
+       if (ar.err == NETDB_SUCCESS) {
+               *name = strdup(ar.ent->h_name); 
+               free_hostent(ar.ent); 
+       }
+       return (ar.err);
+}
+
+static int read_roots(const char *file)
+{
+       FILE    *roots = fopen(file,"r");
+       char    buf[BUFSIZ];
+       int     cnt = 0;
+
+       if (!roots) {
+               perror(file);
+               return 1;
+       }
+
+       while (!feof(roots)) {
+               char    *nl;
+               fgets(buf,sizeof buf,roots);
+               nl = strchr(buf,'\n');
+               if (nl) *nl = 0;
+
+               super_users = realloc(super_users, (cnt+1) * sizeof super_users[0]);
+               super_users[cnt] = strdup(buf);
+               super_users[++cnt] = NULL;
+       }
+
+       fclose(roots);
+
+       return 0;
+}
+
+static int amIroot(const char *subj)
+{
+       int     i;
+
+       if (!subj) return 0;
+       for (i=0; super_users && super_users[i]; i++) 
+               if (strcmp(subj,super_users[i]) == 0) return 1;
+
+       return 0;
+}
+
+static int parse_limits(char *opt, int *j_limit, int *e_limit, int *size_limit)
+{
+       return (sscanf(opt, "%d:%d:%d", j_limit, e_limit, size_limit) == 3);
+}
+
+static unsigned long   clnt_dispatched=0, clnt_accepted=0;
+static void    *mycred;
+static int     server_sock,store_sock;
+
+static int check_mkdir(const char *);
+
+int main(int argc,char *argv[])
+{
+       int     fd,i;
+       struct sockaddr_in      a;
+       struct sigaction        sa;
+       sigset_t        sset;
+       char    *mysubj = NULL;
+       int     opt;
+       char    *cert,*key,*port;
+       char    *name,pidfile[PATH_MAX] = EDG_BKSERVERD_PIDFILE;
+       int     sock_slave[2];
+       FILE    *fpid;
+       key_t   semkey;
+       time_t  cert_mtime = 0,key_mtime = 0;
+       edg_wll_Context ctx;
+
+       name = strrchr(argv[0],'/');
+       if (name) name++; else name = argv[0];
+
+       asprintf(&port,"%d",GLITE_WMSC_JOBID_DEFAULT_PORT);
+       cert = key = cadir = vomsdir = NULL;
+
+/* no magic here: 1 month, 3 and 7 days */
+       purge_timeout[EDG_WLL_PURGE_JOBSTAT_OTHER] = 60*60*24*31;       
+       purge_timeout[EDG_WLL_JOB_CLEARED] = 60*60*24*3;
+       purge_timeout[EDG_WLL_JOB_ABORTED] = 60*60*24*7;
+       purge_timeout[EDG_WLL_JOB_CANCELLED] = 60*60*24*7;
+
+/* no magic here: 1 month, 3 and 7 days */
+       purge_timeout[EDG_WLL_PURGE_JOBSTAT_OTHER] = 60*60*24*31;       
+       purge_timeout[EDG_WLL_JOB_CLEARED] = 60*60*24*3;
+       purge_timeout[EDG_WLL_JOB_ABORTED] = 60*60*24*7;
+       purge_timeout[EDG_WLL_JOB_CANCELLED] = 60*60*24*7;
+
+/* no magic here: 1 month, 3 and 7 days */
+       purge_timeout[EDG_WLL_PURGE_JOBSTAT_OTHER] = 60*60*24*31;       
+       purge_timeout[EDG_WLL_JOB_CLEARED] = 60*60*24*3;
+       purge_timeout[EDG_WLL_JOB_ABORTED] = 60*60*24*7;
+       purge_timeout[EDG_WLL_JOB_CANCELLED] = 60*60*24*7;
+
+       if (geteuid()) snprintf(pidfile,sizeof pidfile,"%s/edg-bkserverd.pid",
+                       getenv("HOME"));
+
+       while ((opt = getopt_long(argc,argv,"a:c:k:C:V:p:drm:ns:l:L:N:i:S:D:X:",opts,NULL)) != EOF) switch (opt) {
+               case 'a': fake_host = strdup(optarg); break;
+               case 'c': cert = optarg; break;
+               case 'k': key = optarg; break;
+               case 'C': cadir = optarg; break;
+               case 'V': vomsdir = optarg; break;
+               case 'p': free(port); port = strdup(optarg); break;
+               case 'd': debug = 1; break;
+               case 'r': rgma_export = 1; break;
+               case 'm': dbstring = optarg; break;
+               case 'n': noAuth = 1; break;
+               case 's': slaves = atoi(optarg); break;
+               case 'l': semaphores = atoi(optarg); break;
+               case 'S': purgeStorage = optarg; break;
+               case 'D': dumpStorage = optarg; break;
+               case 'L':
+                       if ( !parse_limits(optarg, &hardJobsLimit, &hardEventsLimit, &hardRespSizeLimit) )
+                       {
+                               usage(name);
+                               return 1;
+                       }
+                       break;
+               case 'N': notif_duration = atoi(optarg) * (60*60); break;
+               case 'X': notif_ilog_socket_path = strdup(optarg); break;
+               case 'i': strcpy(pidfile,optarg); break;
+               case 'R': if (super_users) {
+                                 fprintf(stderr,"%s: super-users already defined, second occurence ignored\n",
+                                                 argv[0]);
+                                 break;
+                         }
+                         super_users = malloc(2 * sizeof super_users[0]);
+                         super_users[0] = optarg;
+                         super_users[1] = NULL;
+                         break;
+               case 'F': if (super_users) {
+                                 fprintf(stderr,"%s: super-users already defined, second occurence ignored\n",
+                                                 argv[0]);
+                                 break;
+                         }
+                         if (read_roots(optarg)) return 1;
+                         break;
+               case 'x': noIndex = atoi(optarg);
+                         if (noIndex < 0 || noIndex > 2) { usage(name); return 1; }
+                         break;
+               case 'P': strict_locking = 1;
+                         break;
+               case '?': usage(name); return 1;
+       }
+
+       if (optind < argc) { usage(name); return 1; }
+
+       setlinebuf(stdout);
+       setlinebuf(stderr);
+
+       dprintf(("Master pid %d\n",getpid()));
+       fpid = fopen(pidfile,"r");
+       if (fpid) {
+               int     opid = -1;
+               if (fscanf(fpid,"%d",&opid) == 1) {
+                       if (!kill(opid,0)) {
+                               fprintf(stderr,"%s: another instance running, pid = %d\n",argv[0],opid);
+                               return 1;
+                       } else if (errno != ESRCH) {
+                               perror("kill()"); return 1;
+                       }
+               }
+               fclose(fpid);
+       } else if (errno != ENOENT) { perror(pidfile); return 1; }
+
+       fpid = fopen(pidfile,"w");
+       if (!fpid) { perror(pidfile); return 1; }
+       fprintf(fpid,"%d",getpid());
+       fclose(fpid);
+
+       semkey = ftok(pidfile,0);
+
+       if (!debug) for (fd=3; fd<OPEN_MAX; fd++) close(fd);
+
+       if (check_mkdir(dumpStorage)) exit(1);
+       if (check_mkdir(purgeStorage)) exit(1);
+
+       if (semaphores == -1) semaphores = slaves;
+       semset = semget(semkey,0,0);
+       if (semset >= 0) semctl(semset,0,IPC_RMID);
+       semset = semget(semkey,semaphores,IPC_CREAT | 0600);
+       if (semset < 0) { perror("semget()"); return 1; }
+       dprintf(("Using %d semaphores, set id %d\n",semaphores,semset));
+       for (i=0; i<semaphores; i++) {
+               struct sembuf   s;
+
+               s.sem_num = i; s.sem_op = 1; s.sem_flg = 0;
+               if (semop(semset,&s,1) == -1) { perror("semop()"); return 1; }
+       }
+
+       if (fake_host) {
+               char    *p = strchr(fake_host,':');
+               if (p) {
+                       *p = 0;
+                       fake_port = atoi(p+1);
+               } else fake_port = atoi(port);
+       }
+       else {
+               char    buf[300];
+
+               if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS)   {
+                       dprintf(("[%d]: Unable to initialize Globus common module\n",getpid()));
+                       if (!debug) syslog(LOG_CRIT,"Unable to initialize Globus common module\n");
+               }
+
+               globus_libc_gethostname(buf,sizeof buf);
+               buf[sizeof buf - 1] = 0;
+               fake_host = strdup(buf);
+               fake_port = atoi(port); 
+       }
+
+       dprintf(("server address: %s:%d\n",fake_host,fake_port));
+
+       server_sock = socket(PF_INET,SOCK_STREAM,0);
+       if (server_sock<0) { perror("socket()"); return 1; }
+
+       a.sin_family = AF_INET;
+       a.sin_port = htons(atoi(port));
+       a.sin_addr.s_addr = INADDR_ANY;
+
+       setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+       if (bind(server_sock,(struct sockaddr *) &a,sizeof(a))) { 
+               char    buf[100];
+               snprintf(buf,sizeof(buf),"bind(%d)",atoi(port));
+               perror(buf);
+               return 1;
+       }
+
+       if (listen(server_sock,CON_QUEUE)) { perror("listen()"); return 1; }
+
+       store_sock = socket(PF_INET,SOCK_STREAM,0);
+       if (store_sock<0) { perror("socket()"); return 1; }
+
+       a.sin_family = AF_INET;
+       a.sin_port = htons(atoi(port)+1);
+       a.sin_addr.s_addr = INADDR_ANY;
+
+       setsockopt(store_sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+       if (bind(store_sock,(struct sockaddr *) &a,sizeof(a))) {
+               char    buf[100];
+               snprintf(buf,sizeof(buf),"bind(%d)",atoi(port)+1);
+               perror(buf);
+               return 1;
+       }
+       if (listen(store_sock,CON_QUEUE)) { perror("listen()"); return 1; }
+
+       if (socketpair(AF_UNIX, SOCK_STREAM, 0, sock_slave)) {
+               perror("socketpair()");
+               return 1;
+       }
+
+       dprintf(("Initializing SSL ...\n"));
+       edg_wlc_SSLInitialization();
+       if (!cert || !key) fprintf(stderr,"%s: key or certificate file not specified - unable to watch them for changes!\n",argv[0]);
+
+       if (cadir) setenv("X509_CERT_DIR",cadir,1);
+       mycred = edg_wll_ssl_init(SSL_VERIFY_PEER,1,cert,key,0,0);
+       edg_wll_ssl_get_my_subject_base(mycred,&mysubj);
+       if (mysubj) {
+               int     i;
+               dprintf(("Server identity: %s\n",mysubj));
+
+               for (i=0; super_users && super_users[i]; i++);
+               super_users = realloc(super_users,(i+2) * sizeof *super_users);
+               super_users[i] = mysubj;
+               super_users[i+1] = NULL;
+       }
+       else dprintf(("Running unauthenticated\n"));
+
+       if (noAuth) dprintf(("Promiscuous mode\n"));
+       dprintf(("Listening at %d,%d (accepting protocols: " COMP_PROTO " and compatible) ...\n",atoi(port),atoi(port)+1));
+
+       if (!dbstring) dbstring = getenv("LBDB");
+
+       /* Just check the database and let it be. The slaves do the job. */
+       edg_wll_InitContext(&ctx);
+       wait_for_open(ctx,dbstring);
+
+       if (edg_wll_DBCheckVersion(ctx)) {
+               char    *et,*ed;
+               edg_wll_Error(ctx,&et,&ed);
+
+               fprintf(stderr,"%s: open database: %s (%s)\n",argv[0],et,ed);
+               return 1;
+       }
+       edg_wll_Close(ctx);
+       edg_wll_FreeContext(ctx);
+
+       if (!debug) {
+               if (daemon(1,0) == -1) {
+                       perror("deamon()");
+                       exit(1);
+               }
+
+               fpid = fopen(pidfile,"w");
+               if (!fpid) { perror(pidfile); return 1; }
+               fprintf(fpid,"%d",getpid());
+               fclose(fpid);
+
+               openlog(name,LOG_PID,LOG_DAEMON);
+       } else {
+               setpgid(0, getpid());
+       }
+
+       memset(&sa,0,sizeof(sa)); assert(sa.sa_handler == NULL);
+       sa.sa_handler = catchsig;
+       sigaction(SIGINT,&sa,NULL);
+       sigaction(SIGTERM,&sa,NULL);
+
+       sa.sa_handler = catch_chld;
+       sigaction(SIGCHLD,&sa,NULL);
+
+       sa.sa_handler = SIG_IGN;
+       sigaction(SIGUSR1,&sa,NULL);
+
+       sigemptyset(&sset);
+       sigaddset(&sset,SIGCHLD);
+       sigaddset(&sset,SIGTERM);
+       sigaddset(&sset,SIGINT);
+       sigprocmask(SIG_BLOCK,&sset,NULL);
+
+       for (i=0; i<slaves; i++) slave(mycred,sock_slave[1]);
+
+       while (!die) {
+               fd_set  fds;
+               int     ret,mx;
+               struct timeval  watch_to = { WATCH_TIMEOUT, 0 };
+               
+
+               FD_ZERO(&fds);
+               FD_SET(server_sock,&fds);
+               FD_SET(store_sock,&fds);
+               FD_SET(sock_slave[0],&fds);
+
+               switch (edg_wll_ssl_watch_creds(key,cert,&key_mtime,&cert_mtime)) {
+                       void    *newcred;
+                       case 0: break;
+                       case 1: newcred = edg_wll_ssl_init(SSL_VERIFY_PEER,1,cert,key,0,0);
+                               if (newcred) {
+                                       dprintf(("reloading credentials"));
+                                       edg_wll_ssl_free(mycred);
+                                       mycred = newcred;
+                                       kill(0,SIGUSR1);
+                               }
+                               else {
+                                       dprintf(("reloading credentials failed"));
+                               }
+
+                               break;
+                       case -1: dprintf(("edg_wll_ssl_watch_creds failed\n"));
+                                break;
+               }
+                       
+               mx = server_sock;
+               if (mx < store_sock) mx = store_sock;
+               if (mx < sock_slave[0]) mx = sock_slave[0];
+               sigprocmask(SIG_UNBLOCK,&sset,NULL);
+               ret = select(mx+1,&fds,NULL,NULL,&watch_to);
+               sigprocmask(SIG_BLOCK,&sset,NULL);
+
+// XXX is needed?              ctx->p_tmp_timeout.tv_sec = MASTER_TIMEOUT;
+
+               if (ret == -1 && errno != EINTR) {
+                       if (debug) perror("select()");
+                       else syslog(LOG_CRIT,"select(): %m");
+                       return 1;
+               }
+
+               if (child_died) {
+                       int     pid;
+                       while ((pid=waitpid(-1,NULL,WNOHANG))>0) {
+                               if (!die) {
+                                       int newpid = slave(mycred,sock_slave[1]);
+                                       dprintf(("[master] Servus mortuus [%d] miraculo resurrexit [%d]\n",pid,newpid));
+                               }
+                       }
+                       child_died = 0;
+                       continue;
+               }
+
+               if (die) continue;
+
+               if (FD_ISSET(server_sock,&fds) && dispatchit(sock_slave[0],server_sock,0)) continue;
+               if (FD_ISSET(store_sock,&fds) && dispatchit(sock_slave[0],store_sock,1)) continue;
+               
+               if (FD_ISSET(sock_slave[0],&fds)) {
+/* slave accepted a request */
+                       unsigned long   a;
+
+                       if ((recv(sock_slave[0],&a,sizeof(a),MSG_WAITALL) == sizeof(a))
+                               && a<=clnt_dispatched
+                               && (a>clnt_accepted || clnt_accepted>clnt_dispatched)
+                       ) clnt_accepted = a;
+               }
+
+       }
+
+       dprintf(("[master] Terminating on signal %d\n",die));
+       if (!debug) syslog(LOG_INFO,"Terminating on signal %d\n",die);
+       kill(0,die);
+       semctl(semset,0,IPC_RMID,0);
+       unlink(pidfile);
+       free(port);
+       edg_wll_ssl_free(mycred);
+       return 0;
+}
+
+static int dispatchit(int sock_slave,int sock,int store)
+{
+       struct sockaddr_in a;
+       int     conn;
+       unsigned char *pom;
+       int     alen,ret;
+
+       alen=sizeof(a);
+       conn = accept(sock,(struct sockaddr *) &a,&alen);
+
+       if (conn<0) { 
+               if (debug) {
+                       perror("accept()"); return 1; 
+               } else {
+                       syslog(LOG_ERR,"accept(): %m");
+                       sleep(5);
+                       return -1;
+               }
+       }
+
+       alen=sizeof(a);
+       getpeername(conn,(struct sockaddr *)&a,&alen);
+       pom = (char *) &a.sin_addr.s_addr;
+
+       dprintf(("[master] %s connection from %d.%d.%d.%d:%d\n",store?"store":"query",
+               (int) pom[0],(int) pom[1],(int) pom[2],(int) pom[3], ntohs(a.sin_port)));
+
+       ret = 0;
+       if ((clnt_dispatched<clnt_accepted      /* wraparound */
+               || clnt_dispatched-clnt_accepted < slaves*SLAVE_OVERLOAD)
+               && !(ret=do_sendmsg(sock_slave,conn,clnt_dispatched++,store))) {
+                       /* all done */; 
+                       dprintf(("[master] Dispatched %lu, last known served %lu\n",clnt_dispatched-1,clnt_accepted));
+               }
+       else {
+               clnt_reject(mycred,conn);
+               dprintf(("[master] Reject due to overload\n"));
+       }
+       close(conn);
+       if (ret) {
+               perror("sendmsg()");
+               if (!debug) syslog(LOG_ERR,"sendmsg(): %m");
+       }
+       return 0;
+}
+
+
+static int slave(void *mycred,int sock)
+{
+       edg_wll_Context ctx;
+       int     conn = -1,pid;
+       sigset_t        sset;
+       void    *mysql;
+       char    *server_name = NULL;
+       int     conn_cnt = 0;
+       int     h_errno;
+       struct sigaction        sa;
+       int     sockflags;
+       struct timeval  client_done,client_start;
+
+       if ((pid = fork())) return pid;
+
+       srandom(getpid()+time(NULL));
+
+       close(server_sock);
+       close(store_sock);
+
+       sigemptyset(&sset);
+       sigaddset(&sset,SIGTERM);
+       sigaddset(&sset,SIGINT);
+       sigaddset(&sset,SIGUSR1);
+
+       memset(&sa,0,sizeof sa);
+       sa.sa_handler = catchsig;
+       sigaction(SIGUSR1,&sa,NULL);
+
+               if ((sockflags = fcntl(sock, F_GETFL, 0)) < 0 ||
+                       fcntl(sock, F_SETFL, sockflags | O_NONBLOCK) < 0)
+       {
+               dprintf(("[%d] fcntl(master_sock): %s\n",getpid(),strerror(errno)));
+               if (!debug) syslog(LOG_CRIT,"fcntl(master_sock): %m");
+               exit(1);
+       }
+
+       edg_wll_InitContext(&ctx);
+
+       dprintf(("[%d] Spawned, opening database ...\n",getpid()));
+       if (!dbstring) dbstring = getenv("LBDB");
+
+       wait_for_open(ctx,dbstring);
+
+       mysql = ctx->mysql;
+       if (edg_wll_QueryJobIndices(ctx,&job_index,NULL)) {
+               char    *et,*ed;
+               edg_wll_Error(ctx,&et,&ed);
+
+               dprintf(("[%d]: query_job_indices(): %s: %s, no custom indices available\n",getpid(),et,ed));
+               if (!debug) syslog(LOG_ERR,"[%d]: query_job_indices(): %s: %s, no custom indices available\n",getpid(),et,ed);
+               free(et);
+               free(ed);
+               job_index = NULL;
+       }
+       edg_wll_FreeContext(ctx);
+
+       if (job_index) {
+               int i,j, k, maxncol, ncol;
+               ncol = maxncol = 0;
+               for (i=0; job_index[i]; i++) {
+                       for (j=0; job_index[i][j].attr; j++) maxncol++;
+               }
+               job_index_cols = calloc(maxncol+1, sizeof(edg_wll_IColumnRec));
+               for (i=0; job_index[i]; i++) {
+                       for (j=0; job_index[i][j].attr; j++) {
+                               for (k=0; k<ncol && edg_wll_CmpColumn(&job_index_cols[k].qrec, &job_index[i][j]); k++);
+                               if (k==ncol) {
+                                       job_index_cols[ncol].qrec = job_index[i][j];
+                                       if (job_index[i][j].attr == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                               job_index_cols[ncol].qrec.attr_id.tag =
+                                                       strdup(job_index[i][j].attr_id.tag);
+                                       }
+                                       job_index_cols[ncol].colname =
+                                               edg_wll_QueryRecToColumn(&job_index_cols[ncol].qrec);
+                                       ncol++;
+                               }
+                       }
+               }
+               job_index_cols[ncol].qrec.attr = EDG_WLL_QUERY_ATTR_UNDEF;
+               job_index_cols[ncol].colname = NULL;
+       }
+
+       
+       while (!die && (conn_cnt < SLAVE_CONNS_MAX || conn >= 0)) {
+               fd_set  fds;
+               int     alen,store,max = sock,newconn = -1;
+               int     connflags,kick_client = 0;
+               unsigned long   seq;
+               X509    *peer;
+               struct  timeval dns_to = {DNS_TIMEOUT, 0},
+                               check_to = {SLAVE_CHECK_SIGNALS, 0},
+                               total_to = { TOTAL_CLNT_TIMEOUT,0 },
+                               client_to = { CLNT_TIMEOUT,0 }, now;
+               char    *name = NULL;
+               struct  sockaddr_in     a;
+
+
+               FD_ZERO(&fds);
+               FD_SET(sock,&fds);
+               if (conn >= 0) FD_SET(conn,&fds);
+               if (conn > sock) max = conn;
+       
+               sigprocmask(SIG_UNBLOCK,&sset,NULL);
+               switch (select(max+1,&fds,NULL,NULL,&check_to)) {
+                       case -1:
+                               if (errno != EINTR) {
+                                       dprintf(("[%d] select(): %s\n",getpid(),strerror(errno)));
+                                       if (!debug) syslog(LOG_CRIT,"select(): %m");
+                                       exit(1);
+                               }
+                               continue;
+                       case 0: if (conn < 0) continue;
+                       default: break;
+               }
+               sigprocmask(SIG_BLOCK,&sset,NULL);
+
+               gettimeofday(&now,NULL);
+               if (conn >= 0 && (check_timeout(&client_to,client_done,now) ||
+                               check_timeout(&total_to,client_start,now)))
+                                       kick_client = 1;
+
+               if (conn >= 0 && !kick_client && FD_ISSET(conn,&fds)) {
+       /* serve the request */
+
+                       dprintf(("[%d] incoming request\n",getpid()));
+
+                       ctx->p_tmp_timeout.tv_sec = SLAVE_TIMEOUT;
+                       ctx->p_tmp_timeout.tv_usec = 0;
+
+                       if (total_to.tv_sec < ctx->p_tmp_timeout.tv_sec)
+                               /* XXX: sec = && usec < never happens */
+                       {
+                               ctx->p_tmp_timeout.tv_sec = total_to.tv_sec;
+                               ctx->p_tmp_timeout.tv_usec = total_to.tv_usec;
+                       }
+
+                       if (store ? edg_wll_StoreProto(ctx) : edg_wll_ServerHTTP(ctx)) { 
+                               char    *errt,*errd;
+                               errt = errd = NULL;
+
+                               switch (edg_wll_Error(ctx,&errt,&errd)) {
+                                       case ETIMEDOUT:
+                                               /* fallthrough */
+                                       case EDG_WLL_ERROR_SSL:
+                                       case EPIPE:
+                                               dprintf(("[%d] %s (%s)\n",getpid(),errt,errd));
+                                               if (!debug) syslog(LOG_ERR,"%s (%s)",errt,errd);
+                                               /* fallthrough */
+                                       case ENOTCONN:
+                                               edg_wll_ssl_close(ctx->connPool[ctx->connToUse].ssl);
+                                               ctx->connPool[ctx->connToUse].ssl = NULL;       /* XXX: is it necessary ? */
+                                               edg_wll_FreeContext(ctx);
+                                               ctx = NULL;
+                                               close(conn);
+                                               conn = -1;
+                                               free(errt); free(errd);
+                                               dprintf(("[%d] Connection closed\n",getpid()));
+                                               continue;
+                                               break;
+                                       case ENOENT:
+                                               /* fallthrough */
+                                       case EINVAL:
+                                               /* fallthrough */
+                                       case EPERM:
+                                       case EEXIST:
+                                       case EDG_WLL_ERROR_NOINDEX:
+                                       case E2BIG:
+                                               dprintf(("[%d] %s (%s)\n",getpid(),errt,errd));
+                                               if (!debug) syslog(LOG_ERR,"%s (%s)",errt,errd);
+                                               break; /* no action for non-fatal errors */
+                                       default:
+                                               dprintf(("[%d] %s (%s)\n",getpid(),errt,errd));
+                                               if (!debug) syslog(LOG_CRIT,"%s (%s)",errt,errd);
+                                               exit(1);
+                               } 
+                               free(errt); free(errd);
+                       }
+                       
+                       dprintf(("[%d] request done\n",getpid()));
+                       gettimeofday(&client_done,NULL);
+                       continue;
+
+               } 
+
+
+               if (FD_ISSET(sock,&fds) && conn_cnt < SLAVE_CONNS_MAX) {
+                       if (conn >= 0) usleep(100000 + 1000 * (random() % 200));
+                       if (do_recvmsg(sock,&newconn,&seq,&store)) switch (errno) {
+                               case EINTR: /* XXX: signals are blocked */
+                               case EAGAIN: continue;
+                               default: dprintf(("[%d] recvmsg(): %s\n",getpid(),strerror(errno)));
+                                       if (!debug) syslog(LOG_CRIT,"recvmsg(): %m\n");
+                                       exit(1);
+                       }
+                       kick_client = 1;
+               }
+
+               if (kick_client && conn >= 0) {
+                       if (ctx->connPool[ctx->connToUse].ssl) {
+                               struct timeval  to = { 0, CLNT_REJECT_TIMEOUT };
+                               edg_wll_ssl_close_timeout(ctx->connPool[ctx->connToUse].ssl,&to);
+                               ctx->connPool[ctx->connToUse].ssl = NULL;
+                       }
+                       edg_wll_FreeContext(ctx);
+                       close(conn); /* XXX: should not harm */
+                       conn = -1;
+                       dprintf(("[%d] Idle connection closed\n",getpid()));
+               }
+
+               if (newconn >= 0) {
+                       conn = newconn;
+                       gettimeofday(&client_start,NULL);
+                       client_done.tv_sec = client_start.tv_sec;
+                       client_done.tv_usec = client_start.tv_usec;
+
+                       switch (send(sock,&seq,sizeof(seq),0)) {
+                               case -1:
+                                       if (debug) perror("send()");
+                                       else syslog(LOG_CRIT,"send(): %m\n");
+                                       exit(1);
+                               case sizeof(seq): break;
+                               default: dprintf(("[%d] send(): incomplete message\n",getpid()));
+                                       exit(1);
+                       }
+       
+                       dprintf(("[%d] serving %s %lu\n",getpid(),store?"store":"query",seq));
+                       conn_cnt++;
+       
+                       connflags = fcntl(conn, F_GETFL, 0);
+                       if (fcntl(conn, F_SETFL, connflags | O_NONBLOCK) < 0) {
+                               dprintf(("[%d] can't set O_NONBLOCK mode (%s), closing.\n",
+                                                       getpid(), strerror(errno)));
+                               if (!debug) syslog(LOG_ERR,"can't set O_NONBLOCK mode (%s), closing.\n",
+                                               strerror(errno));
+       
+                               close(conn);
+                               conn = -1;
+                               continue;
+                       }
+       
+                       edg_wll_InitContext(&ctx);
+       
+                       /* Shared structures (pointers) */
+                       ctx->mysql = mysql;
+                       ctx->job_index_cols = (void*) job_index_cols;
+                       ctx->job_index = job_index; 
+       
+                       ctx->notifDuration = notif_duration;
+                       ctx->purgeStorage = strdup(purgeStorage);
+                       ctx->dumpStorage = strdup(dumpStorage);
+                       ctx->hardJobsLimit = hardJobsLimit;
+                       ctx->hardEventsLimit = hardEventsLimit;
+                       ctx->semset = semset;
+                       ctx->semaphores = semaphores;
+                       if (noAuth) ctx->noAuth = 1;
+                       ctx->rgma_export = rgma_export;
+                       memcpy(ctx->purge_timeout,purge_timeout,sizeof ctx->purge_timeout);
+       
+                       ctx->p_tmp_timeout.tv_sec = SLAVE_TIMEOUT;
+       
+                       ctx->poolSize = 1;
+                       ctx->connPool = calloc(1,sizeof(edg_wll_ConnPool));
+                       ctx->connToUse = 0;
+       
+                       alen = sizeof(a);
+                       getpeername(conn,(struct sockaddr *)&a,&alen);
+                       ctx->connPool[ctx->connToUse].peerName = strdup(inet_ntoa(a.sin_addr));
+                       ctx->connPool[ctx->connToUse].peerPort = ntohs(a.sin_port);
+
+                       /* not a critical operation, do not waste all SLAVE_TIMEOUT */
+                       switch (h_errno = asyn_gethostbyaddr(&name,(char *) &a.sin_addr.s_addr,sizeof(a.sin_addr.s_addr),
+                                               AF_INET,&dns_to)) {
+                               case NETDB_SUCCESS:
+                                       if (name) dprintf(("[%d] connection from %s:%d (%s)\n", getpid(),
+                                                               inet_ntoa(a.sin_addr), ntohs(a.sin_port), name));
+                                       free(ctx->connPool[ctx->connToUse].peerName);
+                                       ctx->connPool[ctx->connToUse].peerName = name;
+                                       name = NULL;
+                                       break;
+                               default:
+                                       if (debug) fprintf(stderr,"gethostbyaddr(%s): %s", inet_ntoa(a.sin_addr), hstrerror(h_errno));
+                                       /* else syslog(LOG_ERR,"gethostbyaddr(%s): %s", inet_ntoa(a.sin_addr), hstrerror(h_errno)); */
+                                       dprintf(("[%d] connection from %s:%d\n", getpid(), inet_ntoa(a.sin_addr), ntohs(a.sin_port)));
+                                       break;
+                       }
+       
+                       gettimeofday(&now,0);
+                       if ( decrement_timeout(&ctx->p_tmp_timeout, client_start, now) ) {
+                               if (debug) fprintf(stderr,"gethostbyaddr() timeout");
+                               else syslog(LOG_ERR,"gethostbyaddr(): timeout");
+                               free(name);
+                               continue;
+                       }
+                               
+       
+                       if (fake_host) {
+                               ctx->srvName = strdup(fake_host);
+                               ctx->srvPort = fake_port;
+                       }
+                       else {
+                               alen = sizeof(a);
+                               getsockname(conn,(struct sockaddr *) &a,&alen);
+       
+                               dns_to.tv_sec = DNS_TIMEOUT;
+                               dns_to.tv_usec = 0;
+                               switch (h_errno = asyn_gethostbyaddr(&name,(char *) &a.sin_addr.s_addr,sizeof(a.sin_addr.s_addr),
+                                                       AF_INET,&dns_to)) {
+                                       case NETDB_SUCCESS:
+                                               ctx->srvName = name;
+                                               if (server_name != NULL) {
+                                                       if (strcmp(name, server_name)) {
+                                                               if (debug) fprintf(stderr, "different server endpoint names (%s,%s),"
+                                                                                       " check DNS PTR records\n", name, server_name);
+                                                               else syslog(LOG_ERR,"different server endpoint names (%s,%s),"
+                                                                                       " check DNS PTR records\n", name, server_name);
+                                                       }
+                                               } else {
+                                                       server_name = strdup(name);
+                                               }
+                                               break;
+                                       default:
+                                               if (debug) fprintf(stderr, "gethostbyaddr(%s): %s", inet_ntoa(a.sin_addr), hstrerror(h_errno));
+                                               else syslog(LOG_ERR,"gethostbyaddr(%s): %s", inet_ntoa(a.sin_addr), hstrerror(h_errno));
+                                               if (server_name != NULL) {
+                                                        ctx->srvName = strdup(server_name);
+                                               } else {
+                                                       /* only "GET /jobid" requests refused */
+                                               }
+                                               break;
+                               }
+                               ctx->srvPort = ntohs(a.sin_port);
+                       }
+       
+                       if (!(ctx->connPool[ctx->connToUse].ssl = edg_wll_ssl_accept(mycred,conn,&ctx->p_tmp_timeout))) {
+                               dprintf(("[%d] SSL hanshake failed, closing.\n",getpid()));
+                               if (!debug) syslog(LOG_ERR,"SSL hanshake failed");
+       
+                               close(conn);
+                               conn = -1;
+                               edg_wll_FreeContext(ctx);
+                               continue;
+                       } else if ((peer = SSL_get_peer_certificate(ctx->connPool[ctx->connToUse].ssl)) != NULL) {
+                               X509_NAME       *s = X509_get_subject_name(peer);
+       
+                               proxy_get_base_name(s);
+                               free(ctx->peerName);
+                               ctx->peerName = X509_NAME_oneline(s,NULL,0);
+                               ctx->peerProxyValidity = ASN1_UTCTIME_mktime(X509_get_notAfter(peer));
+                               X509_free(peer);
+       
+                               dprintf(("[%d] client DN: %s\n",getpid(),ctx->peerName));
+                               edg_wll_GetVomsGroups(ctx, vomsdir, cadir);
+                               if (debug && ctx->vomsGroups.len > 0) {
+                                  int i;
+       
+                                  dprintf(("[%d] client's VOMS groups:\n",getpid()));
+                                  for (i = 0; i < ctx->vomsGroups.len; i++)
+                                     dprintf(("\t%s:%s\n", 
+                                              ctx->vomsGroups.val[i].vo,
+                                              ctx->vomsGroups.val[i].name));
+                               }
+       
+                       } else dprintf(("[%d] annonymous client\n",getpid()));
+       
+                       /* used also to reset start_time after edg_wll_ssl_accept! */
+                       /* gettimeofday(&start_time,0); */
+       
+       
+                       ctx->noAuth = noAuth || amIroot(ctx->peerName);
+                       switch (noIndex) {
+                               case 0: ctx->noIndex = 0; break;
+                               case 1: ctx->noIndex = amIroot(ctx->peerName); break;
+                               case 2: ctx->noIndex = 1; break;
+                       }
+                       ctx->strict_locking = strict_locking;
+               }
+
+       }
+
+       if (die) {
+               dprintf(("[%d] Terminating on signal %d\n",getpid(),die));
+               if (!debug) syslog(LOG_INFO,"Terminating on signal %d",die);
+       }
+       dprintf(("[%d] Terminating after %d connections\n",getpid(),conn_cnt));
+       if (!debug) syslog(LOG_INFO,"Terminating after %d connections",conn_cnt);
+       exit(0);
+}
+
+static void wait_for_open(edg_wll_Context ctx, const char *dbstring)
+{
+       char    *dbfail_string1, *dbfail_string2;
+
+       dbfail_string1 = dbfail_string2 = NULL;
+
+       while (edg_wll_Open(ctx, (char *) dbstring)) {
+               char    *errt,*errd;
+
+               if (dbfail_string1) free(dbfail_string1);
+               edg_wll_Error(ctx,&errt,&errd);
+               asprintf(&dbfail_string1,"%s (%s)\n",errt,errd);
+               if (dbfail_string1 != NULL) {
+                       if (dbfail_string2 == NULL || strcmp(dbfail_string1,dbfail_string2)) {
+                               if (dbfail_string2) free(dbfail_string2);
+                               dbfail_string2 = dbfail_string1;
+                               dbfail_string1 = NULL;
+                               dprintf(("[%d]: %s\nStill trying ...\n",getpid(),dbfail_string2));
+                               if (!debug) syslog(LOG_ERR,dbfail_string2);
+                       }
+               }
+               sleep(5);
+       }
+
+       if (dbfail_string1) free(dbfail_string1);
+       if (dbfail_string2 != NULL) {
+               free(dbfail_string2);
+               dprintf(("[%d]: DB connection established\n",getpid()));
+               if (!debug) syslog(LOG_INFO,"DB connection established\n");
+       }
+}
+
+static int check_mkdir(const char *dir)
+{
+       struct stat     sbuf;
+       
+       if (stat(dir,&sbuf)) {
+               if (errno == ENOENT) {
+                       if (mkdir(dir, S_IRWXU)) {
+                               dprintf(("[%d] %s: %s\n",
+                                       getpid(),dir,strerror(errno)));
+
+                               if (!debug) syslog(LOG_CRIT,"%s: %m",dir);
+                               return 1;
+                       }
+               }
+               else {
+                       dprintf(("[%d] %s: %s\n",getpid(),strerror(errno)));
+                       if (!debug) syslog(LOG_CRIT,"%s: %m",dir);
+                       return 1;
+               }
+       }
+       else if (S_ISDIR(sbuf.st_mode)) return 0;
+       else {
+               dprintf(("[%d] %s: not a directory\n", getpid(),dir));
+               if (!debug) syslog(LOG_CRIT,"%s: not a directory",dir);
+               return 1;
+       }
+}
diff --git a/org.glite.lb.server/src/db_store.c b/org.glite.lb.server/src/db_store.c
new file mode 100644 (file)
index 0000000..6ff49cc
--- /dev/null
@@ -0,0 +1,91 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/events_parse.h"
+#include "store.h"
+#include "lbs_db.h"
+#include "lock.h"
+
+extern int debug;
+
+/* XXX */
+#define use_db 1
+
+extern int edg_wll_NotifMatch(edg_wll_Context, const edg_wll_JobStat *);
+
+int
+db_store(edg_wll_Context ctx,char *ucs, char *event)
+{
+  edg_wll_Event *ev;
+  int  seq;
+  int   err;
+  edg_wll_JobStat      newstat;
+
+  ev = NULL;
+
+  edg_wll_ResetError(ctx);
+  memset(&newstat,0,sizeof newstat);
+
+  if(edg_wll_ParseEvent(ctx, event, &ev))
+    goto err;
+
+  /* XXX: if event type is user tag, convert the tag name to lowercase!
+   *     (not sure whether to convert a value too is reasonable
+   *     or keep it 'case sensitive')
+   */
+  if ( ev->any.type == EDG_WLL_EVENT_USERTAG )
+  {
+       int i;
+       for ( i = 0; ev->userTag.name[i] != '\0'; i++ )
+         ev->userTag.name[i] = tolower(ev->userTag.name[i]);
+  }
+  
+  if(ev->any.user == NULL)
+    ev->any.user = strdup(ucs);
+
+  if(use_db) {
+    if (ctx->strict_locking && edg_wll_LockJob(ctx,ev->any.jobId)) goto err;
+    if(edg_wll_StoreEvent(ctx, ev,&seq))
+      goto err;
+  }
+
+  if (debug) { fputs(event,stderr); fputc('\n',stderr); }
+
+  if (!ctx->strict_locking && edg_wll_LockJob(ctx,ev->any.jobId)) goto err;
+
+  if ( ev->any.type == EDG_WLL_EVENT_CHANGEACL )
+    err = edg_wll_UpdateACL(ctx, ev->any.jobId,
+                       ev->changeACL.user_id, ev->changeACL.user_id_type,
+                       ev->changeACL.permission, ev->changeACL.permission_type,
+                       ev->changeACL.operation);
+  else
+    err = edg_wll_StepIntState(ctx,ev->any.jobId, ev, seq,&newstat);
+
+  if (edg_wll_UnlockJob(ctx,ev->any.jobId)) goto err;
+  if (err) goto err;
+
+  if (newstat.state) {
+         edg_wll_NotifMatch(ctx,&newstat);
+         edg_wll_FreeStatus(&newstat);
+  }
+
+  edg_wll_FreeEvent(ev);
+  free(ev);
+
+  return(0);
+
+ err:
+  if(ev) {
+    edg_wll_FreeEvent(ev);
+    free(ev);
+  }
+  
+  return edg_wll_Error(ctx,NULL,NULL);
+}
+
diff --git a/org.glite.lb.server/src/dump.c b/org.glite.lb.server/src/dump.c
new file mode 100644 (file)
index 0000000..d3c5dd3
--- /dev/null
@@ -0,0 +1,192 @@
+#ident "$Header$"
+
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+#include <unistd.h>
+
+#include "glite/lb/trio.h"
+#include "glite/wms/jobid/cjobid.h"
+
+#include "glite/lb/context-int.h"
+#include "glite/lb/events_parse.h"
+#include "glite/lb/ulm_parse.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/dump.h"
+
+#include "lbs_db.h"
+#include "query.h"
+#include "get_events.h"
+#include "server_state.h"
+#include "purge.h"
+
+static int handle_specials(edg_wll_Context,time_t *);
+
+#define sizofa(a) (sizeof(a)/sizeof((a)[0]))
+
+int edg_wll_DumpEvents(edg_wll_Context ctx,const edg_wll_DumpRequest *req,edg_wll_DumpResult *result)
+{
+       char    *from_s, *to_s, *stmt, *time_s;
+       char    *tmpfname;
+       time_t  start,end;
+       edg_wll_Stmt    q = NULL;
+       char            *res[10];
+       int     event;
+       edg_wll_Event   e;
+       int     ret,dump = 2;   /* TODO: manage dump file */
+       time_t  from = req->from,to = req->to;
+
+       from_s = to_s = stmt = NULL;
+       memset(res,0,sizeof res);
+       memset(&e,0,sizeof e);
+
+       time(&start);
+       edg_wll_ResetError(ctx);
+
+       if ( (dump = edg_wll_CreateTmpDumpFile(ctx, &tmpfname)) == -1 )
+               return edg_wll_Error(ctx, NULL, NULL);
+
+       if (handle_specials(ctx,&from) || handle_specials(ctx,&to))
+       {
+               unlink(tmpfname);
+               return edg_wll_Error(ctx,NULL,NULL);
+       }
+
+       from_s = strdup(edg_wll_TimeToDB(from));
+       to_s = strdup(edg_wll_TimeToDB(to));
+
+       trio_asprintf(&stmt,
+                       "select event,dg_jobid,code,prog,host,u.cert_subj,time_stamp,usec,level,arrived "
+                       "from events e,users u,jobs j "
+                       "where u.userid=e.userid "
+                       "and j.jobid = e.jobid "
+                       "and j.dg_jobid like 'https://%|Ss:%d%%' "
+                       "and arrived > '%|Ss' and arrived <= '%|Ss' "
+                       "order by arrived",
+                       ctx->srvName,ctx->srvPort,
+                       from_s,to_s);
+
+       if (edg_wll_ExecStmt(ctx,stmt,&q) < 0) goto clean;
+
+       while ((ret = edg_wll_FetchRow(q,res)) > 0) {
+               assert(ret == sizofa(res));
+               event = atoi(res[0]); free(res[0]); res[0] = NULL;
+
+               if (convert_event_head(ctx,res+1,&e)
+                       || edg_wll_get_event_flesh(ctx,event,&e))
+               {
+                       char    *et,*ed;
+                       int     i;
+
+               /* Most likely sort of internal inconsistency. 
+                * Must not be fatal -- just complain
+                */
+                       edg_wll_Error(ctx,&et,&ed);
+                       fprintf(stderr,"%s event %d: %s (%s)\n",res[1],event,et,ed);
+                       syslog(LOG_WARNING,"%s event %d: %s (%s)",res[1],event,et,ed);
+                       free(et); free(ed);
+                       for (i=0; i<sizofa(res); i++) free(res[i]);
+                       edg_wll_ResetError(ctx);
+               }
+               else {
+                       char    *event_s = edg_wll_UnparseEvent(ctx,&e);
+                       char    arr_s[100];
+                       int             len, written, total;
+
+                       strcpy(arr_s, "DG.ARRIVED=");
+                       edg_wll_ULMTimevalToDate(e.any.arrived.tv_sec,
+                                                       e.any.arrived.tv_usec,
+                                                       arr_s+strlen("DG.ARRIVED="));
+                       len = strlen(arr_s);
+                       total = 0;
+                       while (total != len) {
+                               written = write(dump,arr_s+total,len-total);
+                               if (written < 0 && errno != EAGAIN) {
+                                       edg_wll_SetError(ctx,errno,"writing dump file");
+                                       free(event_s);
+                                       goto clean;
+                               }
+                               total += written;
+                       }
+                       write(dump, " ", 1);
+                       len = strlen(event_s);
+                       total = 0;
+                       while (total != len) {
+                               written = write(dump,event_s+total,len-total);
+                               if (written < 0 && errno != EAGAIN) {
+                                       edg_wll_SetError(ctx,errno,"writing dump file");
+                                       free(event_s);
+                                       goto clean;
+                               }
+                               total += written;
+                       }
+                       write(dump,"\n",1);
+                       free(event_s);
+               }
+               edg_wll_FreeEvent(&e); memset(&e,0,sizeof e);
+       }
+
+       time(&end);
+/* XXX: get rid of apostrophes returned by edg_wll_TimeToDB() */
+       time_s = strdup(edg_wll_TimeToDB(start));
+       time_s[strlen(time_s)-1] = 0;
+       edg_wll_SetServerState(ctx,EDG_WLL_STATE_DUMP_START,time_s+1);
+       free(time_s);
+       
+       time_s = strdup(edg_wll_TimeToDB(end));
+       time_s[strlen(time_s)-1] = 0;
+       edg_wll_SetServerState(ctx,EDG_WLL_STATE_DUMP_END,time_s+1);
+       free(time_s);
+
+       result->from = from;
+       result->to = to;
+
+       edg_wll_CreateDumpFileFromTmp(ctx, tmpfname, &(result->server_file));
+       unlink(tmpfname);
+
+clean:
+       edg_wll_FreeEvent(&e);
+       edg_wll_FreeStmt(&q);
+
+       free(stmt);
+       free(from_s);
+       free(to_s);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static int handle_specials(edg_wll_Context ctx,time_t *t)
+{
+       char    *time_s;
+       int     ret;
+
+       edg_wll_ResetError(ctx);
+       switch (*t) {
+               case EDG_WLL_DUMP_NOW:
+                       time(t);
+                       return 0;
+               case EDG_WLL_DUMP_LAST_START:
+               case EDG_WLL_DUMP_LAST_END:
+                       switch (ret = edg_wll_GetServerState(ctx,
+                                       *t == EDG_WLL_DUMP_LAST_START ? 
+                                               EDG_WLL_STATE_DUMP_START:
+                                               EDG_WLL_STATE_DUMP_END,
+                                       &time_s))
+                       {
+                               case ENOENT: *t = 0; 
+                                            edg_wll_ResetError(ctx);
+                                            break;
+                               case 0: *t = edg_wll_DBToTime(time_s); 
+                                       assert(*t >= 0);
+                                       break;
+                               default: break;
+                       }
+                       break;
+               default: if (*t < 0) return edg_wll_SetError(ctx,EINVAL,"special time limit unrecognized");
+       }
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
diff --git a/org.glite.lb.server/src/get_events.c.T b/org.glite.lb.server/src/get_events.c.T
new file mode 100644 (file)
index 0000000..a4b2f6e
--- /dev/null
@@ -0,0 +1,157 @@
+#ident "$Header$"
+
+/*
+@@@AUTO
+*/
+@@@LANG: C
+
+/* Helper functions for getting events from the LB database *
+ * XXX: lots of stuff still hadcoded:
+ *             there's mapping db.columns <-> union event fields
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "get_events.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+#include "glite/lb/events_parse.h"
+
+static void edg_wll_set_event_field(edg_wll_Event *,char *,char *);
+static void edg_wll_set_event_field_warn(edg_wll_Event *,char *,char *);
+
+int edg_wll_get_event_flesh(edg_wll_Context ctx,int n,edg_wll_Event *e)
+{
+       char    *jobid = edg_wlc_JobIdGetUnique(e->any.jobId),
+               *q = NULL,*nameval[2];
+       edg_wll_Stmt    sh;
+       int     ret,t;
+       const char      *tables[] = { "short_fields","long_fields" };
+
+       edg_wll_ResetError(ctx);
+
+       for (t=0; t<=1; t++) {
+               trio_asprintf(&q,"select name,value from %s "
+                               "where jobid = '%|Ss' and event = %d ",
+                               tables[t],jobid,n);
+
+               if ((ret=edg_wll_ExecStmt(ctx,q,&sh)) < 0) goto cleanup;
+
+               while ((ret=edg_wll_FetchRow(sh,nameval)) > 0) {
+                       edg_wll_set_event_field(e,nameval[0],nameval[1]);
+                       free(nameval[0]);
+                       /* XXX: nameval[1] freed in edg_wll_set_event_field
+                        * if necessary
+                        */
+               }
+
+               if (ret<0) goto cleanup;
+               edg_wll_FreeStmt(&sh);
+               free(q); q=NULL;
+       }
+
+       ret=edg_wll_CheckEvent(ctx,e);
+
+cleanup:
+       if (sh) edg_wll_FreeStmt(&sh);
+       free(jobid);
+       free(q);
+
+       if (ret) { edg_wll_FreeEvent(e); memset(e,0,sizeof *e); }
+       return ret;
+}
+
+
+/* print/log  warning for database inconsistency */
+
+static void edg_wll_set_event_field_warn(
+               edg_wll_Event   *event,
+               char            *name,
+               char            *value)
+{
+       char *e = edg_wll_EventToString(event->any.type);
+
+       fprintf(stderr, "edg_wll_set_event_field: bad field:"
+               "code=\"%s\" name=\"%s\" value=\"%s\"\n",
+               e, name, value);
+       free(e);
+       /* XXX edg_wll_Log */
+}
+
+/* set event structure field */
+
+static void edg_wll_set_event_field(
+               edg_wll_Event   *event,
+               char            *name,
+               char            *value)
+{
+/* XXX: where's the best place to hande it? */
+       if (!strcasecmp(name,"SRC_INSTANCE")) {
+               event->any.src_instance = value;
+               return;
+       }
+
+/* XXX: handled separately, should go to event_head one day */
+       if (!strcasecmp(name,"SEQCODE")) {
+               event->any.seqcode = value;
+               return;
+       }
+
+@@@{
+       for my $n (getAllFieldsOrdered $event) {
+               my @occ = getFieldOccurence $event $n;
+               next if $#occ == 0 && $occ[0] eq '_common_';
+               selectType $event $occ[0];
+               my $f = selectField $event $n;
+               my $name = getName $f;
+               my $lcname = lc $name;
+               gen qq{
+!      if (!strcasecmp(name,"$lcname")) \{
+!              switch (event->any.type) \{
+};
+               for (@occ) {
+                       next if $_ eq '_common_';
+                       selectType $event $_;
+                       $f = selectField $event $n;
+                       my $fucname = ucfirst $n;
+                       my $uctype = uc $_;
+                       my $flctype = lcfirst $_;
+                       my $frs = $f->{codes} ?
+                               "event->$flctype.$name = edg_wll_StringTo$_${fucname}(value);" :
+                               fromString $f 'value',"event->$flctype.$name";
+                       gen qq{
+!                      case EDG_WLL_EVENT_$uctype: $frs break;
+};
+               }
+               gen qq{
+!                      default: edg_wll_set_event_field_warn(event,name,value); break;
+!              \} /* switch */
+!              free(value);
+!              return;
+!      \}
+};
+       }
+@@@}
+
+       edg_wll_set_event_field_warn(event,name,value);
+       free(value);
+       return;
+}
+               
+
+int compare_events_by_tv(const void *a, const void *b)
+{
+       const edg_wll_Event *e = (edg_wll_Event *)a;
+       const edg_wll_Event *f = (edg_wll_Event *)b;
+
+       if (e->any.timestamp.tv_sec < f->any.timestamp.tv_sec) return -1;
+       if (e->any.timestamp.tv_sec > f->any.timestamp.tv_sec) return 1;
+       if (e->any.timestamp.tv_usec < f->any.timestamp.tv_usec) return -1;
+       if (e->any.timestamp.tv_usec > f->any.timestamp.tv_usec) return 1;
+       return 0;
+}
diff --git a/org.glite.lb.server/src/get_events.h b/org.glite.lb.server/src/get_events.h
new file mode 100644 (file)
index 0000000..590b61e
--- /dev/null
@@ -0,0 +1,24 @@
+#ident "$Header$"
+
+/* Internal functions for getting event sets from the LB database */
+#include "lbs_db.h"
+
+#if 0  /* rel 1 */
+char *edg_wll_jobid_to_user( edg_wll_Context, char *);
+void edg_wll_set_event_field_warn( edg_wll_Event *, char *, char *);
+void edg_wll_set_event_field( edg_wll_Event *, char *, char *);
+int edg_wll_get_events_restricted( edg_wll_Context, edg_wlc_JobId, char *, int, int, char *, edg_wll_Event **);
+#define edg_wll_get_events(ctx,job,md5,emin,emax,ret) \
+       edg_wll_get_events_restricted((ctx),(job),(md5),(emin),(emax),NULL,(ret))
+int edg_wll_last_event( edg_wll_Context, char *);
+int compare_events_by_tv(const void *, const void *);
+#endif
+
+int edg_wll_get_event_flesh(edg_wll_Context,int,edg_wll_Event *);
+int edg_wll_QueryEventsServer(edg_wll_Context,int,const edg_wll_QueryRec **,const edg_wll_QueryRec **,edg_wll_Event **);
+
+int edg_wll_QueryJobsServer(edg_wll_Context, const edg_wll_QueryRec **, int, edg_wlc_JobId **, edg_wll_JobStat **);
+
+void edg_wll_SortEvents(edg_wll_Event *);
+int edg_wll_compare_seq(const char *, const char *);
+
diff --git a/org.glite.lb.server/src/il_notification.c b/org.glite.lb.server/src/il_notification.c
new file mode 100644 (file)
index 0000000..5ac8c53
--- /dev/null
@@ -0,0 +1,388 @@
+/**
+ * il_notification.c
+ *   - implementation of IL API calls for notifications
+ *
+ */
+#ident "$Header$"
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#include "glite/lb/context-int.h"
+#include "glite/lb/notifid.h"
+#include "glite/lb/events_parse.h"
+#include "glite/lb/escape.h"
+#include "glite/lb/log_proto.h"
+
+#include "il_notification.h"
+#include "lb_xml_parse.h"
+
+
+
+#define FCNTL_ATTEMPTS         5
+#define FCNTL_TIMEOUT          1
+#define FILE_PREFIX             "/tmp/notif_events"
+#define DEFAULT_SOCKET          "/tmp/notif_interlogger.sock"
+
+char *notif_ilog_socket_path = DEFAULT_SOCKET;
+
+#define tv_sub(a,b) {\
+       (a).tv_usec -= (b).tv_usec;\
+       (a).tv_sec -= (b).tv_sec;\
+       if ((a).tv_usec < 0) {\
+               (a).tv_sec--;\
+               (a).tv_usec += 1000000;\
+       }\
+}
+
+static
+int
+notif_create_ulm(
+       edg_wll_Context context,
+       edg_wll_NotifId reg_id,
+       const char      *host,
+       const uint16_t  port,
+       const char      *owner,
+       const char      *notif_data,
+       char            **ulm_data,
+       char            **reg_id_s)
+{
+       int             ret;
+       edg_wll_Event   *event=NULL;
+
+       *ulm_data = NULL;
+       *reg_id_s = NULL;
+
+       event = edg_wll_InitEvent(EDG_WLL_EVENT_NOTIFICATION);
+
+       gettimeofday(&event->any.timestamp,0);
+       if (context->p_host) event->any.host = strdup(context->p_host);
+       event->any.level = context->p_level;
+       event->any.source = context->p_source;
+       if (context->p_instance) event->notification.src_instance = strdup(context->p_instance);
+       event->notification.notifId = reg_id;
+       if (owner) event->notification.owner = strdup(owner);
+       if (host) event->notification.dest_host = strdup(host);
+       event->notification.dest_port = port;
+       if (notif_data) event->notification.jobstat = strdup(notif_data);
+
+       if ((*ulm_data = edg_wll_UnparseNotifEvent(context,event)) == NULL) {
+               edg_wll_SetError(context, ret = ENOMEM, "edg_wll_UnparseNotifEvent()"); 
+               goto out;
+       }
+
+       if((*reg_id_s = edg_wll_NotifIdGetUnique(reg_id)) == NULL) {
+               edg_wll_SetError(context, ret = ENOMEM, "edg_wll_NotifIdGetUnique()");
+               goto out;
+       }
+
+       ret = 0;
+
+out:
+       if(event) { 
+               edg_wll_FreeEvent(event);
+               free(event);
+       }
+       if(ret) edg_wll_UpdateError(context, ret, "notif_create_ulm()");
+       return(ret);
+}
+
+
+static
+int
+notif_save_to_file(edg_wll_Context     context,
+                  const char         *event_file,
+                  const char         *ulm_data,
+                  long               *filepos)
+{
+       int ret;
+       FILE *outfile;
+       int filedesc;
+       struct flock filelock;
+       int i, filelock_status=-1;
+
+       for(i=0; i < FCNTL_ATTEMPTS; i++) {
+               /* fopen and properly handle the filelock */
+               if ((outfile = fopen(event_file,"a")) == NULL) {
+                       edg_wll_SetError(context, ret = errno, "fopen()");
+                       goto out;
+               }
+               if ((filedesc = fileno(outfile)) == -1) {
+                       edg_wll_SetError(context, ret = errno, "fileno()");
+                       goto out1;
+               }
+               filelock.l_type = F_WRLCK;
+               filelock.l_whence = SEEK_SET;
+               filelock.l_start = 0;
+               filelock.l_len = 0;
+               filelock_status=fcntl(filedesc, F_SETLK, &filelock);
+               if(filelock_status < 0) {
+                       switch(errno) {
+                       case EAGAIN:
+                       case EACCES:
+                       case EINTR:
+                               /* lock is held by someone else */
+                               sleep(FCNTL_TIMEOUT);
+                               break;
+                       default:
+                               /* other error */
+                               edg_wll_SetError(context, ret=errno, "fcntl()");
+                               goto out1;
+                       }
+               } else {
+                       /* lock acquired, break out of the loop */
+                       break;
+               }
+       }
+       if (fseek(outfile, 0, SEEK_END) == -1) {
+               edg_wll_SetError(context, ret = errno, "fseek()");
+               goto out1;
+       }
+       if ((*filepos=ftell(outfile)) == -1) {
+               edg_wll_SetError(context, ret = errno, "ftell()");
+               goto out1;
+       }
+       /* write, flush and sync */
+       if (fputs(ulm_data, outfile) == EOF) {
+               edg_wll_SetError(context, ret = errno, "fputs()");
+               goto out1;
+       }       
+       if (fflush(outfile) == EOF) {
+               edg_wll_SetError(context, ret = errno, "fflush()");
+               goto out1;
+       }
+       if (fsync(filedesc) < 0) { /* synchronize */
+               edg_wll_SetError(context, ret = errno, "fsync()");
+               goto out1;
+       } 
+
+       ret = 0;
+out1:
+       /* close and unlock */
+       fclose(outfile); 
+out:
+       if(ret) edg_wll_UpdateError(context, ret, "notif_save_to_file()");
+       return(ret);
+}
+
+
+static 
+ssize_t 
+socket_write_full(edg_wll_Context  context,
+                 int              sock,
+                 void            *buf,
+                 size_t           bufsize,
+                 struct timeval  *timeout,
+                 ssize_t         *total)
+{
+       int ret = 0;
+        ssize_t        len;
+
+        *total = 0;
+        while (bufsize > 0) {
+
+               fd_set  fds;
+               struct timeval  to,before,after;
+               
+               if (timeout) {
+                       memcpy(&to, timeout, sizeof(to));
+                       gettimeofday(&before, NULL);
+               }
+
+               len = write(sock, buf, bufsize);
+               while (len <= 0) {
+                       FD_ZERO(&fds);
+                       FD_SET(sock, &fds);
+                       if (select(sock+1, &fds, NULL, NULL, timeout ? &to : NULL) < 0) {
+                               edg_wll_SetError(context, ret = errno, "select()");
+                               goto out;
+                       }
+                       len = write(sock, buf, bufsize);
+               }
+               if (timeout) {
+                       gettimeofday(&after,NULL);
+                       tv_sub(after, before);
+                       tv_sub(*timeout,after);
+                       if (timeout->tv_sec < 0) {
+                               timeout->tv_sec = 0;
+                               timeout->tv_usec = 0;
+                       }
+               }
+               
+                if (len < 0) {
+                       edg_wll_SetError(context, ret = errno, "write()");
+                       goto out;
+               }
+
+               bufsize -= len;
+               buf += len;
+                *total += len;
+        }
+
+       ret = 0;
+out:
+       if(ret) edg_wll_UpdateError(context, ret, "socket_write_full()");
+        return ret;
+}
+
+
+static 
+int
+notif_send_socket(edg_wll_Context       context,
+                 long                  filepos,
+                 const char           *ulm_data)
+{
+       int ret;
+       struct sockaddr_un saddr;
+       int msg_sock, flags, count;
+       struct timeval timeout;
+
+       timeout.tv_sec = EDG_WLL_LOG_TIMEOUT_MAX;
+        timeout.tv_usec = 0;   
+
+       msg_sock = socket(PF_UNIX, SOCK_STREAM, 0);
+       if(msg_sock < 0) {
+               edg_wll_SetError(context, ret = errno, "socket()");
+               goto out;
+       }
+
+       memset(&saddr, 0, sizeof(saddr));
+       saddr.sun_family = AF_UNIX;
+       strcpy(saddr.sun_path, notif_ilog_socket_path);
+
+       if ((flags = fcntl(msg_sock, F_GETFL, 0)) < 0 ||
+           fcntl(msg_sock, F_SETFL, flags | O_NONBLOCK) < 0) {
+               edg_wll_SetError(context, ret = errno, "fcntl()");
+               goto out1;
+       }
+
+       if(connect(msg_sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+               if(errno != EISCONN) {
+                       edg_wll_SetError(context, ret = errno, "connect()");
+                       goto out1;
+               }
+       }
+
+       if (socket_write_full(context, msg_sock, &filepos, sizeof(filepos), &timeout, &count) < 0) {
+               ret = errno; 
+               goto out1;
+       }
+
+       if (socket_write_full(context, msg_sock, (void*)ulm_data, strlen(ulm_data), &timeout, &count) < 0) {
+               ret = errno; 
+               goto out1;
+       }
+
+       ret = 0;
+
+out1:
+       close(msg_sock);
+out:
+       if(ret) edg_wll_UpdateError(context, ret, "notif_send_socket()");
+       return(ret);
+}
+
+
+int
+edg_wll_NotifSend(edg_wll_Context       context,
+                 edg_wll_NotifId       reg_id,
+                 const char           *host,
+                  int                   port,
+                 const char           *owner,
+                  const char           *notif_data)
+{
+       int ret;
+       long filepos;
+       char *ulm_data, *reg_id_s, *event_file;
+
+       if((ret=notif_create_ulm(context, 
+                                reg_id, 
+                                host, 
+                                port, 
+                                owner, 
+                                notif_data,
+                                &ulm_data,
+                                &reg_id_s))) {
+               goto out;
+       }
+
+       asprintf(&event_file, "%s.%s", FILE_PREFIX, reg_id_s);
+       if(event_file == NULL) {
+               edg_wll_SetError(context, ret=ENOMEM, "asprintf()");
+               goto out;
+       }
+
+       if((ret=notif_save_to_file(context,
+                                  event_file,
+                                  ulm_data,
+                                  &filepos))) {
+               goto out;
+       }
+
+       if((ret=notif_send_socket(context,
+                                 filepos,
+                                 ulm_data))) {
+               goto out;
+       }
+       ret = 0;
+
+out:
+       if(ulm_data) free(ulm_data);
+       if(reg_id_s) free(reg_id_s);
+       if(ret) edg_wll_UpdateError(context, ret, "edg_wll_NotifSend()");
+       return(ret);
+}
+
+
+int
+edg_wll_NotifJobStatus(edg_wll_Context context,
+                      edg_wll_NotifId  reg_id,
+                      const char      *host,
+                       int              port,
+                      const char      *owner,
+                      const edg_wll_JobStat notif_job_stat)
+{
+       int ret=0;
+       char   *xml_data, *xml_esc_data=NULL;
+
+       if(edg_wll_JobStatusToXML(context, notif_job_stat, &xml_data)) 
+               goto out;
+       
+       if((xml_esc_data = edg_wll_EscapeXML(xml_data)) == NULL) {
+               edg_wll_SetError(context, ret=ENOMEM, "edg_wll_EscapeXML()");
+               goto out;
+       }
+
+       ret=edg_wll_NotifSend(context, reg_id, host, port, owner, xml_esc_data);
+
+out:
+       if(xml_data) free(xml_data);
+       if(xml_esc_data) free(xml_esc_data);
+       if(ret) edg_wll_UpdateError(context, ret, "edg_wll_NotifJobStatus()");
+       return(ret);
+}
+
+
+int 
+edg_wll_NotifChangeDestination(edg_wll_Context context,
+                               edg_wll_NotifId reg_id,
+                               const char      *host,
+                               int             port)
+{
+       return(edg_wll_NotifSend(context, reg_id, host, port, "", ""));
+}
+
+
+int
+edg_wll_NotifCancelRegId(edg_wll_Context context,
+                        edg_wll_NotifId reg_id)
+{
+       return(edg_wll_NotifSend(context, reg_id, NULL, 0, "", ""));
+}
+
diff --git a/org.glite.lb.server/src/il_notification.h b/org.glite.lb.server/src/il_notification.h
new file mode 100644 (file)
index 0000000..3c842c9
--- /dev/null
@@ -0,0 +1,97 @@
+#ifndef IL_NOTIFICATION_H
+#define IL_NOTIFICATION_H
+
+#ident "$Header$"
+
+/* needed for the edg_wll_NotifId */
+#include "glite/lb/notification.h"
+/* import the edg_wll_JobStat structure */
+#include "glite/lb/jobstat.h"
+
+#ifdef __cplusplus
+#extern "C" {
+#endif
+
+extern char *notif_ilog_socket_path;
+
+/** Send ULM notification string to interlogger.
+ * Stores notification to file according to registration id and send it
+ * to interlogger using local socket.
+ * \param reg_id  registration id
+ * \param host,port address to deliver the notification to.
+ *                  If NULL, it means no further notifications will
+ *                  follow (the client has unregistered). It always
+ *                  overrides previous values (ie. changes the
+ *                  reg_id->client address mapping in interlogger).
+ * \param owner DN of the registration owner, this will be verified
+ *              against client's certificate
+ * \param notif_data ULM formatted notification string, may be NULL,
+ *                   if there is nothing to be sent to client.
+ * \retval 0 OK
+ * \retval EINVAL      bad jobId, unknown event code, or the format
+ *                      string together with the remaining arguments
+ *                      does not form a valid event 
+ * \retval ENOSPC      unable to accept the event due to lack of disk
+ *                      space etc. 
+ * \retval ENOMEM      failed to allocate memory
+ * \retval EAGAIN      non blocking return from the call, the event
+ *                      did not come through socket, but is backed up
+ *                      in file
+ */
+int
+edg_wll_NotifSend(edg_wll_Context       context,
+                 edg_wll_NotifId       reg_id,
+                 const char           *host,
+                  int                   port,
+                 const char           *owner,
+                  const char           *notif_data);
+
+
+/** Send job status notification.
+ * Creates ULM notification string and sends it using
+ * edg_wll_NotifSend(). The job status is encoded into XML and escaped
+ * before inclusion into ULM.
+ * \param reg_id registration id
+ * \param host,port address to deliver the notification to.
+ * \param owner DN of the registration owner, this will be verified
+ *              against client's certificate
+ * \param notif_job_stat structure describing job status
+ * \see edg_wll_NotifSend()
+ */
+int
+edg_wll_NotifJobStatus(edg_wll_Context context,
+                      edg_wll_NotifId  reg_id,
+                      const char      *host,
+                       int              port,
+                      const char      *owner,
+                      const edg_wll_JobStat notif_job_stat);
+
+
+/** Change address for notification delivery.
+ * Creates ULM string and uses edg_wll_NotifSend() to pass it
+ * to interlogger.
+ * \param reg_id registration id
+ * \param host,port new delivery address
+ * \see edg_wll_NotifSend()
+ */
+int 
+edg_wll_NotifChangeDestination(edg_wll_Context context,
+                               edg_wll_NotifId reg_id,
+                               const char      *host,
+                               int             port);
+
+/** Cancel registration.
+ * Creates ULM string and uses edg_wll_NotifSend() to pass it to
+ * interlogger.
+ * \param reg_id registration id
+ */
+int
+edg_wll_NotifCancelRegId(edg_wll_Context context,
+                        edg_wll_NotifId reg_id);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/org.glite.lb.server/src/index.c.T b/org.glite.lb.server/src/index.c.T
new file mode 100644 (file)
index 0000000..b3cbd18
--- /dev/null
@@ -0,0 +1,227 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "glite/lb/context-int.h"
+#include "lbs_db.h"
+#include "index.h"
+
+extern int debug;
+
+#define const_len(c)   (sizeof((c))-1)
+
+/* TODO: 
+   - better error recovery (skip unrecognised indices etc.)
+   - leaks memory on errors
+*/
+
+int edg_wll_QueryJobIndices(edg_wll_Context ctx,edg_wll_QueryRec *** index_out,char ***keys_out)
+{
+       edg_wll_QueryRec        **idx = NULL;
+       edg_wll_Stmt            stmt = NULL;
+
+       int     i,j,ret;
+
+       static const char *built_in_indices[] = {
+               "PRIMARY",
+               "parent_job",
+               NULL
+       };
+
+/* XXX: "show index from" columns. Matches at least MySQL 4.0.11 */
+
+       char    *showcol[12];
+       int     Key_name,Seq_in_index,Column_name,Sub_part;
+
+       char    **keys = NULL;
+       int     *cols = NULL;
+       char    **col_names = NULL;
+
+       int     nkeys = 0;
+
+       Key_name = Seq_in_index = Column_name = Sub_part = -1;
+
+       if (edg_wll_ExecStmt(ctx,"show index from states",&stmt)<0) 
+               return edg_wll_Error(ctx,NULL,NULL);
+
+       while ((ret = edg_wll_FetchRow(stmt,showcol))) {
+               if (ret < 0) return edg_wll_Error(ctx,NULL,NULL);
+               assert(ret <= sizeof showcol/sizeof showcol[0]);
+
+               if (!col_names) {
+                       col_names = malloc(ret * sizeof col_names[0]);
+                       edg_wll_QueryColumns(stmt,col_names);
+                       for (i=0; i<ret; i++) 
+                               if (!strcasecmp(col_names[i],"Key_name")) Key_name = i;
+                               else if (!strcasecmp(col_names[i],"Seq_in_index")) Seq_in_index = i;
+                               else if (!strcasecmp(col_names[i],"Column_name")) Column_name = i;
+                               else if (!strcasecmp(col_names[i],"Sub_part")) Sub_part = i;
+
+                       assert(Key_name >= 0 && Seq_in_index >= 0 && 
+                                       Column_name >= 0 && Sub_part >= 0);
+
+               }
+
+
+               for (j=0; built_in_indices[j]; j++) {
+                       if (strcasecmp(showcol[Key_name],built_in_indices[j]) == 0) {
+                               for (i=0; i<ret; i++) free(showcol[i]);
+                               goto continue_fetch_index;
+                       }
+               }
+               
+               for (i=0; i<nkeys && strcasecmp(showcol[Key_name],keys[i]); i++);
+
+               if (i == nkeys) {
+                       keys = realloc(keys,(i+2) * sizeof keys[0]);
+                       keys[i] = showcol[Key_name];
+                       keys[i+1] = NULL;
+                       cols = realloc(cols,(i+1) * sizeof cols[0]); 
+                       cols[i] = 0;
+                       idx = realloc(idx,(i+2) * sizeof idx[0]);
+                       idx[i] = idx[i+1] = NULL;
+                       showcol[Key_name] = NULL;
+                       nkeys++;
+               }
+
+               j = atoi(showcol[Seq_in_index])-1;
+               if (cols[i] <= j) {
+                       cols[i] = j+1;
+                       idx[i] = realloc(idx[i],(j+2)*sizeof idx[i][0]);
+                       memset(&idx[i][j+1],0,sizeof idx[i][0]);
+               }
+
+               if (edg_wll_ColumnToQueryRec(showcol[Column_name],&idx[i][j])) {
+                       char    ed[300];
+                       sprintf(ed,"%s(%s): unsupported column",keys[i],showcol[Column_name]);
+                       return edg_wll_SetError(ctx,EINVAL,ed);
+               }
+               else idx[i][j].value.i = atoi(showcol[Sub_part]);
+                       
+               for (i=0; i<ret; i++) free(showcol[i]);
+continue_fetch_index:
+               // just for escaping from nested cycles         
+               ;       /* prevent compiler to complain */
+       }
+
+       edg_wll_FreeStmt(&stmt);
+       free(cols);
+       free(col_names);
+       if (keys_out) *keys_out = keys; else free(keys);
+       *index_out = idx;
+
+       return edg_wll_ResetError(ctx);
+}
+
+int edg_wll_CmpColumn(const edg_wll_QueryRec *r1,const edg_wll_QueryRec *r2)
+{
+       if (r1->attr != r2->attr) return 1;
+       switch (r1->attr) {
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+                       return strcasecmp(r1->attr_id.tag,r2->attr_id.tag);
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       return r1->attr_id.state != r2->attr_id.state;
+               default:
+                       return 0;
+       }
+}
+
+static struct {
+       const char      *name;
+       edg_wll_QueryAttr       attr;
+} std_attrs[] =
+{
+@@@{
+       for my $n ($status->getAllFieldsOrdered) {
+               my $f = selectField $status $n;
+               next unless $f->{index};
+               my $u = uc getName $f;
+               gen "\t{ \"$n\", EDG_WLL_QUERY_ATTR_$u },\n";
+       }
+@@@}
+       { NULL, },
+};
+
+int edg_wll_ColumnToQueryRec(const char *col_name,edg_wll_QueryRec *rec)
+{
+       int     i;
+
+       memset(rec,0,sizeof *rec);
+       if (strncasecmp(col_name,STD_PREFIX,const_len(STD_PREFIX)) == 0) {
+               for (i=0; std_attrs[i].name
+                       && strcasecmp(std_attrs[i].name,col_name+const_len(STD_PREFIX)); i++);
+
+               if (std_attrs[i].name) rec->attr = std_attrs[i].attr;
+       }
+       else if (strncasecmp(col_name,TIME_PREFIX,const_len(TIME_PREFIX)) == 0) {
+               rec->attr_id.state = edg_wll_StringToStat(col_name+const_len(TIME_PREFIX));
+               if (rec->attr_id.state != (edg_wll_JobStatCode) -1) rec->attr = EDG_WLL_QUERY_ATTR_TIME;
+       }
+       else if (strncasecmp(col_name,USR_PREFIX,const_len(USR_PREFIX)) == 0) {
+               rec->attr = EDG_WLL_QUERY_ATTR_USERTAG;
+               rec->attr_id.tag = strdup(col_name+const_len(USR_PREFIX));
+               rec->value.c = NULL;
+       }
+
+       return !rec->attr;
+}
+
+char * edg_wll_QueryRecToColumn(const edg_wll_QueryRec *rec)
+{
+       char    col[100] = "";
+
+       if (rec->attr == EDG_WLL_QUERY_ATTR_USERTAG) {
+               strcpy(col,USR_PREFIX);
+               strcat(col,rec->attr_id.tag);
+       }
+       else if (rec->attr == EDG_WLL_QUERY_ATTR_TIME) {
+               char    *s = edg_wll_StatToString(rec->attr_id.state);
+
+               if (s) {
+                       strcpy(col,TIME_PREFIX);
+                       strcat(col,s);
+                       free(s);
+               }
+       }
+       else {
+               int     i;
+               for (i=0; std_attrs[i].name && std_attrs[i].attr != rec->attr; i++);
+               if (std_attrs[i].name) {
+                       strcpy(col,STD_PREFIX);
+                       strcat(col,std_attrs[i].name);
+               }
+       }
+
+       return col[0] ? strdup(col) : NULL;
+}
+
+char * edg_wll_QueryRecToColumnExt(const edg_wll_QueryRec *rec)
+{
+       char    *intern = edg_wll_QueryRecToColumn(rec),
+               *out;
+
+       if (!intern) return NULL;
+
+       switch (rec->attr) {
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+                       out = strdup(intern+const_len(USR_PREFIX));
+                       break;
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       out = strdup(intern+const_len(TIME_PREFIX));
+                       break;
+               default:
+                       out = strdup(intern+const_len(STD_PREFIX));
+                       break;
+       }
+       free(intern);
+       return out;
+}
+
+void edg_wll_FreeIColumnRec(edg_wll_IColumnRec *icrp)
+{
+       if (icrp->qrec.attr == EDG_WLL_QUERY_ATTR_USERTAG)
+               free(icrp->qrec.attr_id.tag);
+       free(icrp->colname);
+}
+
diff --git a/org.glite.lb.server/src/index.h b/org.glite.lb.server/src/index.h
new file mode 100644 (file)
index 0000000..2e585f2
--- /dev/null
@@ -0,0 +1,27 @@
+
+int edg_wll_QueryJobIndices(edg_wll_Context,edg_wll_QueryRec ***,char ***);
+int edg_wll_ColumnToQueryRec(const char *,edg_wll_QueryRec *);
+char * edg_wll_QueryRecToColumn(const edg_wll_QueryRec *);
+char * edg_wll_QueryRecToColumnExt(const edg_wll_QueryRec *);
+
+int edg_wll_ParseIndexConfig(edg_wll_Context,const char *,edg_wll_QueryRec ***);
+int edg_wll_DumpIndexConfig(edg_wll_Context,const char *,edg_wll_QueryRec * const *);
+
+int edg_wll_CmpColumn(const edg_wll_QueryRec *,const edg_wll_QueryRec *);
+
+typedef struct _edg_wll_IColumnRec {
+       edg_wll_QueryRec        qrec;
+       char *                  colname;
+} edg_wll_IColumnRec;
+
+void edg_wll_FreeIColumnRec(edg_wll_IColumnRec *);
+
+int yylex();
+
+extern int     lex_int;
+extern char    *lex_out;
+extern int     lex_line;
+
+#define STD_PREFIX     "STD_"
+#define USR_PREFIX     "USR_"
+#define TIME_PREFIX    "TIME_"
diff --git a/org.glite.lb.server/src/index_lex.l b/org.glite.lb.server/src/index_lex.l
new file mode 100644 (file)
index 0000000..fa552a1
--- /dev/null
@@ -0,0 +1,47 @@
+%{
+#ident "$Header$"
+
+#include <string.h>
+
+#include "glite/lb/context-int.h"
+#include "index_parse.h"
+#include "index.h"
+
+char   *lex_out;
+int    lex_int;
+int    lex_line;
+
+int yywrap(void) { return 1; }
+
+#define YY_NO_UNPUT
+
+%}
+
+
+delim  [ \t]
+ws     {delim}+
+string \"[^\"\n]*\"
+digit  [0-9]
+int    {digit}+
+
+%%
+{ws}   {}
+
+JobIndices     return JOB_INDICES;
+type           return TYPE;
+name           return NAME;
+prefixlen      return PREFIX;
+{int}          {
+                       lex_int = atoi(yytext);
+                       return INT;
+               }
+{string}       {
+                       int     len;
+                       lex_out = malloc(len = strlen(yytext)-1);
+                       strncpy(lex_out,yytext+1,len-1);
+                       lex_out[len-1] = 0;
+                       return STRING;
+               }
+\n     lex_line++;
+.              return *yytext;
+
diff --git a/org.glite.lb.server/src/index_parse.y b/org.glite.lb.server/src/index_parse.y
new file mode 100644 (file)
index 0000000..4c9265b
--- /dev/null
@@ -0,0 +1,242 @@
+%{
+#ident "$Header$"
+
+
+#include <stdio.h>
+#include <string.h>
+
+#include "glite/lb/context-int.h"
+
+#include "lbs_db.h"
+#include "index.h"
+
+#define yyerror(x) {}
+
+#define YYDEBUG        1
+
+#define ATTR_TYPE_SYSTEM       "system"
+#define ATTR_TYPE_USER         "user"
+#define ATTR_TYPE_TIME         "time"
+
+static edg_wll_Context parse_ctx;
+static const char      *parse_fname;
+
+#define bailout(msg) \
+{ \
+       char    *buf; \
+ \
+       asprintf(&buf,"%s:%d: %s",parse_fname,lex_line,(msg)); \
+       edg_wll_SetError(parse_ctx,EINVAL,buf); \
+       free(buf); \
+       YYABORT; \
+}
+
+extern FILE *yyin;
+
+edg_wll_QueryRec       **indices_out;
+
+%}
+
+%term JOB_INDICES
+%term STRING
+%term INT
+%term TYPE
+%term NAME
+%term PREFIX
+
+%union
+{
+       char    *s;
+       int     i;
+       edg_wll_QueryRec        qr;
+       edg_wll_QueryRec        *qrl;
+       edg_wll_QueryRec        **qrll;
+       struct elem_attr {
+               int     attr;
+               char    *val;
+       }                       attr;
+}
+
+%type <s> string
+%type <i> int
+%type <qr> job_index_elem
+%type <qrl> job_index job_index_elem_list
+%type <qrll> job_index_list job_indices;
+%type <attr> elem_attr opt_elem_attr
+
+%%
+
+config : '[' job_indices soft_semicolon ']'    { indices_out = $2; }
+       ;
+
+job_indices    : JOB_INDICES '=' '{' job_index_list soft_comma '}' { $$ = $4; }
+               ;
+
+job_index_list : job_index     { $$ = calloc(2,sizeof (*$$)); *$$ = $1; }
+               | job_index_list ',' job_index
+{
+       int     i;
+       for (i=0; $1[i]; i++);
+       $$ = realloc($1,(i+2) * sizeof *$1);
+       $$[i] = $3;
+       $$[i+1] = NULL;
+}
+               ;
+
+job_index      : job_index_elem                { $$ = calloc(2,sizeof (*$$)); memcpy($$,&$1,sizeof $1); }
+               | '{' job_index_elem_list '}'   { $$ = $2; }
+               ;
+
+job_index_elem_list    : job_index_elem        { $$ = calloc(2,sizeof (*$$)); memcpy($$,&$1,sizeof $1); }
+               | job_index_elem_list ',' job_index_elem
+{
+       int     i;
+       for (i=0; $1[i].attr; i++);
+       $$ = realloc($1,(i+2) * sizeof *$1);
+       memcpy($$+i,&$3,sizeof $3);
+       memset($$+i+1,0,sizeof *$$);
+}
+               ;
+
+job_index_elem : '[' elem_attr ';' elem_attr opt_elem_attr ']'
+{
+       char    *name = $2.attr == NAME ? $2.val :
+                               $4.attr == NAME ? $4.val : 
+                               $5.attr == NAME ? $5.val : NULL,
+               *type = $2.attr == TYPE ? $2.val :
+                               $4.attr == TYPE ? $4.val :
+                               $5.attr == TYPE ? $5.val : NULL;
+       int     prefix = $2.attr == PREFIX ? (int) $2.val :
+                               $4.attr == PREFIX ? (int) $4.val :
+                               $5.attr == PREFIX ? (int) $5.val : 0;
+
+
+       if (!name) bailout("`name' required");
+       if (!type) bailout("`type' required");
+
+       if (strcasecmp(type,ATTR_TYPE_SYSTEM) == 0) {
+               char    *name2;
+               asprintf(&name2,STD_PREFIX "%s",name);
+               if (edg_wll_ColumnToQueryRec(name2,&$$)) bailout("unknown attribute");
+               free(name2);
+               free(name);
+       }
+       else if (strcasecmp(type,ATTR_TYPE_USER) == 0) {
+               $$.attr = EDG_WLL_QUERY_ATTR_USERTAG;
+               $$.attr_id.tag = name;
+       }
+       else if (strcasecmp(type,ATTR_TYPE_TIME) == 0) {
+               char    *name2;
+               if (prefix) bailout("PREFIXLEN is not valid with time attributes");
+               asprintf(&name2,TIME_PREFIX "%s",name);
+               if (edg_wll_ColumnToQueryRec(name2,&$$)) bailout("unknown attribute");
+               free(name2);
+               free(name);
+       }
+       else bailout("unknown attr type");
+
+       $$.value.i = prefix;
+}
+               ;
+
+elem_attr      : TYPE '=' string       { $$.attr = TYPE; $$.val = $3; }
+               | NAME '=' string       { $$.attr = NAME; $$.val = $3; }
+               | PREFIX '=' int        { $$.attr = PREFIX; $$.val = (char *) $3; }
+               ;
+
+opt_elem_attr  :       { $$.attr = 0; $$.val = NULL; }
+               |       ';' elem_attr   { $$ = $2; }
+               ;
+
+string         : STRING        { $$ = lex_out; lex_out = NULL; }
+               ;
+
+int            : INT           { $$ = lex_int; }
+               ;
+
+soft_semicolon :
+               | ';'
+               ;
+
+soft_comma     :
+               | ','
+               ;
+
+
+%%
+
+
+/* XXX: uses static variables -- non thread-safe */
+
+int edg_wll_ParseIndexConfig(edg_wll_Context ctx,const char *fname,edg_wll_QueryRec ***out)
+{
+       yyin = strcmp(fname,"-") ? fopen(fname,"r") : stdin;
+       lex_line = 1;
+
+       if (!yyin) return edg_wll_SetError(ctx,errno,fname);
+
+       parse_ctx = ctx;
+       parse_fname = fname;
+       edg_wll_ResetError(ctx);
+
+       /* yydebug = 1; */
+       if (yyparse() && !edg_wll_Error(ctx,NULL,NULL)) {
+               char    buf[100];
+               if (yyin != stdin) fclose(yyin);
+               sprintf(buf,"%s:%d: parse error",fname,lex_line);
+               return edg_wll_SetError(ctx,EINVAL,buf);
+       }
+       if (yyin != stdin) fclose(yyin);
+
+       if (!edg_wll_Error(ctx,NULL,NULL)) *out = indices_out;
+       indices_out = NULL;     /* XXX: memory leak on error but who cares? */
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_DumpIndexConfig(edg_wll_Context ctx,const char *fname,edg_wll_QueryRec * const *idx)
+{
+       int     haveit = 0;
+
+       FILE    *f = strcmp(fname,"-") ? fopen(fname,"w") : stdout;
+
+       if (!f) return edg_wll_SetError(ctx,errno,fname);
+       if (idx && *idx) { haveit = 1; fputs("[\n\tJobIndices = {\n",f); }
+
+       while (idx && *idx) {
+               const edg_wll_QueryRec *i;
+               int     multi = (*idx)[1].attr;
+
+               fputs(multi ? "\t\t{\n" : "\t\t",f);
+
+               for (i=*idx; i->attr; i++) {
+                       char    *cn = edg_wll_QueryRecToColumnExt(i);
+                       char    prefix[100] = "";
+                       char    *type;
+
+                       switch (i->attr) {
+                               case EDG_WLL_QUERY_ATTR_USERTAG: type = ATTR_TYPE_USER; break;
+                               case EDG_WLL_QUERY_ATTR_TIME: type = ATTR_TYPE_TIME; break;
+                               default: type = ATTR_TYPE_SYSTEM; break;
+                       }
+                               
+                       if (i->value.i) sprintf(prefix,"; prefixlen = %d ",i->value.i);
+                       if (multi) fputs("\t\t\t",f);
+                       fprintf(f,"[ type = \"%s\"; name = \"%s\" %s]",type,cn,prefix);
+                       if (multi) fputs(i[1].attr ? ",\n" : "\n",f);
+                       free(cn);
+               }
+
+               if (multi) fputs("\t\t}",f);
+               fputs(idx[1] ? ",\n" : "\n",f);
+
+               idx++;
+       }
+
+       if (haveit) {
+               fputs("\t}\n]\n",f);
+               return edg_wll_ResetError(ctx);
+       }
+       else return edg_wll_SetError(ctx,ENOENT,"no indices");
+}
+
+
diff --git a/org.glite.lb.server/src/jobstat.c b/org.glite.lb.server/src/jobstat.c
new file mode 100644 (file)
index 0000000..6d2e64f
--- /dev/null
@@ -0,0 +1,1250 @@
+#ident "$Header$"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <regex.h>
+#include <syslog.h>
+
+#include "glite/lb/producer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+
+#include "get_events.h"
+#include "store.h"
+#include "lock.h"
+#include "index.h"
+#include "jobstat.h"
+#include "lb_authz.h"
+
+
+extern int debug;
+
+#define DAG_ENABLE     1
+
+#ifndef dprintf
+#define dprintf(x) { if (debug) printf x; }
+#endif
+
+/* TBD: share in whole logging or workload */
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+  
+#define RET_FAIL       0
+#define RET_OK         1
+#define RET_FATAL      RET_FAIL
+#define RET_SOON       2
+#define RET_LATE       3
+#define RET_BADSEQ     4
+#define RET_SUSPECT    5
+#define RET_IGNORE     6
+#define RET_INTERNAL   100
+
+#define rep(a,b) { free(a); a = (b == NULL) ? NULL : strdup(b); }
+#define mov(a,b) { free(a); a = b; b = NULL; }
+
+static void warn (const char* format, ...) UNUSED_VAR ;
+static char *job_owner(edg_wll_Context,char *);
+static char* location_string(const char*, const char*, const char*);
+static int add_stringlist(char ***, const char *) UNUSED_VAR;
+static int add_taglist(edg_wll_TagValue **, const char *, const char *);
+
+
+int edg_wll_intJobStatus(edg_wll_Context, const edg_wlc_JobId, int, intJobStat *, int);
+edg_wll_ErrorCode edg_wll_StoreIntState(edg_wll_Context, intJobStat *, int);
+edg_wll_ErrorCode edg_wll_LoadIntState(edg_wll_Context , edg_wlc_JobId , int, intJobStat **);
+
+int js_enable_store = 1;
+
+/*
+ * Basic manipulations with the internal representation of job state
+ */
+
+static void init_intJobStat(intJobStat *p)
+{
+       memset(p, 0, sizeof(intJobStat));
+       p->pub.jobtype = EDG_WLL_STAT_SIMPLE;
+       p->pub.children_hist = (int*) calloc(1+EDG_WLL_NUMBER_OF_STATCODES, sizeof(int));
+       p->pub.children_hist[0] = EDG_WLL_NUMBER_OF_STATCODES;
+       p->pub.stateEnterTimes = (int*) calloc(1+EDG_WLL_NUMBER_OF_STATCODES, sizeof(int));
+       p->pub.stateEnterTimes[0] = EDG_WLL_NUMBER_OF_STATCODES;
+       /* TBD: generate */
+}
+
+static void destroy_intJobStat_extension(intJobStat *p)
+{
+       free(p->last_seqcode); p->last_seqcode = NULL;
+       free(p->last_cancel_seqcode); p->last_cancel_seqcode = NULL;
+                              p->wontresub = 0;
+}
+
+void destroy_intJobStat(intJobStat *p)
+{
+       edg_wll_FreeStatus(&p->pub);
+       destroy_intJobStat_extension(p);
+       memset(p, 0, sizeof(intJobStat));
+}
+
+#if 0
+static int eval_expect_update(intJobStat *, int *, char **);
+#endif
+
+static int processEvent(intJobStat *, edg_wll_Event *, int, int, char **);
+
+static char* matched_substr(char *, regmatch_t) UNUSED_VAR;
+
+static char* matched_substr(char *in, regmatch_t match)
+{
+       int len;
+       char *s;
+
+       len = match.rm_eo - match.rm_so;
+       s = calloc(1, len + 1);
+       if (s != NULL) {
+               strncpy(s, in + (int)match.rm_so, len);
+       }
+
+       return s;
+}
+
+
+int edg_wll_JobStatus(
+       edg_wll_Context ctx,
+       const edg_wlc_JobId             job,
+       int             flags,
+       edg_wll_JobStat *stat)
+{
+
+/* Local variables */
+       char            *string_jobid;
+       char            *md5_jobid;
+
+       intJobStat      jobstat;
+       intJobStat      *ijsp;
+       int             intErr = 0;
+       int             lockErr;
+       edg_wll_Acl     acl = NULL;
+#if DAG_ENABLE 
+       char            *stmt = NULL;
+#endif 
+
+       edg_wll_ResetError(ctx);
+
+       string_jobid = edg_wlc_JobIdUnparse(job);
+       if (string_jobid == NULL || stat == NULL)
+               return edg_wll_SetError(ctx,EINVAL, NULL);
+       md5_jobid = edg_wlc_JobIdGetUnique(job);
+
+       if ( !(jobstat.pub.owner = job_owner(ctx,md5_jobid)) ) {
+               free(md5_jobid);
+               free(string_jobid);
+               return edg_wll_Error(ctx,NULL,NULL);
+       }
+
+       intErr = edg_wll_GetACL(ctx, job, &acl);
+       if (intErr) {
+               free(md5_jobid);
+               free(string_jobid);
+               return edg_wll_Error(ctx,NULL,NULL);
+       }
+
+       /* authorization check */
+       if ( !(ctx->noAuth) &&
+           (!(ctx->peerName) ||  strcmp(ctx->peerName, jobstat.pub.owner))) {
+               intErr = (acl == NULL) || edg_wll_CheckACL(ctx, acl, EDG_WLL_PERM_READ);
+             if (intErr) {
+                free(string_jobid);
+                free(md5_jobid);
+                free(jobstat.pub.owner); jobstat.pub.owner = NULL;
+                if (acl) {
+                       edg_wll_FreeAcl(acl);
+                       return edg_wll_Error(ctx, NULL, NULL);
+                } else {
+                       return edg_wll_SetError(ctx,EPERM, "not owner, no ACL is set");
+                }
+             }
+       }
+
+       intErr = edg_wll_LoadIntState(ctx, job, -1 /*all events*/, &ijsp);
+       if (!intErr) {
+               *stat = ijsp->pub;
+               destroy_intJobStat_extension(ijsp);
+               free(ijsp);
+
+       } else {
+               lockErr = edg_wll_LockJob(ctx,job);
+               intErr = edg_wll_intJobStatus(ctx, job, flags,&jobstat, js_enable_store && !lockErr);
+               if (!lockErr) {
+                       edg_wll_UnlockJob(ctx,job);
+               }
+       
+               *stat = jobstat.pub;
+               if (intErr) edg_wll_FreeStatus(&jobstat.pub);
+               destroy_intJobStat_extension(&jobstat);
+       }
+       
+       if (intErr) {
+               free(string_jobid);
+               free(md5_jobid);
+               if (acl) edg_wll_FreeAcl(acl);
+               return edg_wll_Error(ctx, NULL, NULL);
+       }
+
+       if (acl) {
+               stat->acl = strdup(acl->string);
+               edg_wll_FreeAcl(acl);
+       }
+
+       if ((flags & EDG_WLL_STAT_CLASSADS) == 0) {
+               char *null = NULL;
+
+               mov(stat->jdl, null);
+               mov(stat->matched_jdl, null);
+               mov(stat->condor_jdl, null);
+               mov(stat->rsl, null);
+       }
+
+#if DAG_ENABLE
+       if (stat->jobtype == EDG_WLL_STAT_DAG) {
+               if (1) {
+                       char *out[2];
+                       edg_wll_Stmt sh;
+                       int num_sub, num_f;
+
+                       if (stat->children_hist == NULL) {
+                               stat->children_hist = (int*) calloc(1+EDG_WLL_NUMBER_OF_STATCODES, sizeof(int));
+                               stat->children_hist[0] = EDG_WLL_NUMBER_OF_STATCODES;
+                       }
+                       if ((flags & EDG_WLL_STAT_CHILDREN) == 0) {
+                               trio_asprintf(&stmt, "SELECT status FROM states "
+                                                       "WHERE parent_job='%|Ss' AND version='%|Ss'",
+                                       md5_jobid, INTSTAT_VERSION);
+                               out[1] = NULL;
+                       } else {
+                               trio_asprintf(&stmt, "SELECT s.status,j.dg_jobid FROM states s,jobs j "
+                                               "WHERE s.parent_job='%|Ss' AND s.version='%|Ss' AND s.jobid=j.jobid",
+                                       md5_jobid, INTSTAT_VERSION);
+                       }
+                       if (stmt != NULL) {
+                               num_sub = edg_wll_ExecStmt(ctx, stmt, &sh);
+                               if (num_sub >=0 ) {
+                                       while ((num_f = edg_wll_FetchRow(sh, out)) == 1
+                                                       || (num_f == 2)) {
+                                               num_f = atoi(out[0]);
+                                               if (num_f > EDG_WLL_JOB_UNDEF && num_f < EDG_WLL_NUMBER_OF_STATCODES)
+                                                       stat->children_hist[num_f+1]++;
+                                               if (out[1] !=NULL) add_stringlist(&stat->children, out[1]);
+                                               free(out[0]); free(out[1]);
+                                       }
+                                       edg_wll_FreeStmt(&sh);
+                               }
+                               free(stmt);
+                       } else goto dag_enomem;
+               }
+               if (flags & EDG_WLL_STAT_CHILDSTAT) {
+                       char *stat_str, *s_out;
+                       edg_wll_Stmt sh;
+                       int num_sub, num_f, i;
+                       intJobStat *js;
+
+                       trio_asprintf(&stmt, "SELECT int_status FROM states WHERE parent_job='%|Ss'"
+                                               " AND version='%|Ss'",
+                                       md5_jobid, INTSTAT_VERSION);
+                       if (stmt != NULL) {
+                               num_sub = edg_wll_ExecStmt(ctx, stmt, &sh);
+                               if (num_sub >=0 ) {
+                                       i = 0;
+                                       stat->children_states = calloc(num_sub+1, sizeof(edg_wll_JobStat));
+                                       if (stat->children_states == NULL) {
+                                               edg_wll_FreeStmt(&sh);
+                                               goto dag_enomem;
+                                       }
+                                       while ((num_f = edg_wll_FetchRow(sh, &stat_str)) == 1
+                                               && i < num_sub) {
+                                               js = dec_intJobStat(stat_str, &s_out);
+                                               if (s_out != NULL && js != NULL) {
+                                                       stat->children_states[i] = js->pub;
+                                                       destroy_intJobStat_extension(js);
+                                                       free(js);
+                                                       i++;
+                                               }
+                                               free(stat_str);
+                                       }
+                                       edg_wll_FreeStmt(&sh);
+                               }
+                               free(stmt);
+                       } else goto dag_enomem;
+               }
+       }
+#endif
+       free(string_jobid);
+       free(md5_jobid);
+       return edg_wll_Error(ctx, NULL, NULL);
+
+#if DAG_ENABLE
+dag_enomem:
+       free(string_jobid);
+       free(md5_jobid);
+       edg_wll_FreeStatus(stat);
+       free(stmt);
+       return edg_wll_SetError(ctx, ENOMEM, NULL);
+#endif
+}
+
+int edg_wll_intJobStatus(
+       edg_wll_Context ctx,
+       const edg_wlc_JobId     job,
+       int                     flags,
+       intJobStat      *intstat,
+       int             update_db)
+{
+
+/* Local variables */
+       char            *string_jobid;
+       char            *md5_jobid;
+
+       int             num_events;
+       edg_wll_Event   *events = NULL;
+
+       int             i, intErr = 0;
+       int             res;
+       int             be_strict = 0;
+       char            *errstring = NULL;
+
+       edg_wll_QueryRec        jqr[2];
+       edg_wll_QueryRec        **jqra;
+
+/* Processing */
+       edg_wll_ResetError(ctx);
+       init_intJobStat(intstat);
+
+       string_jobid = edg_wlc_JobIdUnparse(job);
+       if (string_jobid == NULL || intstat == NULL)
+               return edg_wll_SetError(ctx,EINVAL, NULL);
+
+       /* can be already filled by public edg_wll_JobStat() */
+       if (intstat->pub.owner == NULL) {
+               md5_jobid = edg_wlc_JobIdGetUnique(job);
+               if ( !(intstat->pub.owner = job_owner(ctx,md5_jobid)) ) {
+                       free(md5_jobid);
+                       free(string_jobid);
+                       return edg_wll_Error(ctx,NULL,NULL);
+               }
+               free(md5_jobid);
+       }
+       
+        jqr[0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+        jqr[0].op = EDG_WLL_QUERY_OP_EQUAL;
+        jqr[0].value.j = job;
+        jqr[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+       jqra = (edg_wll_QueryRec **) malloc (2 * sizeof(edg_wll_QueryRec **));
+       jqra[0] = jqr;
+       jqra[1] = NULL;
+
+       if (edg_wll_QueryEventsServer(ctx,1, (const edg_wll_QueryRec **)jqra, NULL, &events)) {
+               free(string_jobid);
+               free(jqra);
+                return edg_wll_Error(ctx, NULL, NULL);
+       }
+       free(jqra);
+
+       for (num_events = 0; events[num_events].type != EDG_WLL_EVENT_UNDEF;
+               num_events++);
+
+       if (num_events == 0) {
+               free(string_jobid);
+               return edg_wll_SetError(ctx,ENOENT,NULL);
+       }
+
+       edg_wll_SortEvents(events);
+
+       for (i = 0; i < num_events; i++) {
+               res = processEvent(intstat, &events[i], i, be_strict, &errstring);
+               if (res == RET_FATAL || res == RET_INTERNAL) { /* !strict */
+                       intErr = 1; break;
+               }
+       }
+       if (intstat->pub.state == EDG_WLL_JOB_UNDEF) {
+               intstat->pub.state = EDG_WLL_JOB_UNKNOWN;
+       }
+
+       free(string_jobid);
+
+       for (i=0; i < num_events ; i++) edg_wll_FreeEvent(&events[i]);
+       free(events);
+
+
+       if (intErr) {
+               destroy_intJobStat(intstat);
+               return edg_wll_SetError(ctx, EDG_WLL_ERROR_SERVER_RESPONSE, NULL);
+       } else {
+               /* XXX intstat->pub.expectUpdate = eval_expect_update(intstat, &intstat->pub.expectFrom); */
+               intErr = edg_wlc_JobIdDup(job, &intstat->pub.jobId);
+               if (!intErr) {
+                       if (update_db) {
+                               int tsq = num_events - 1;
+                               edg_wll_StoreIntState(ctx, intstat, tsq);
+                               /* recheck
+                                * intJobStat *reread;
+                                * edg_wll_LoadIntState(ctx, job, tsq, &reread);
+                                * destroy_intJobStat(reread);
+                               */
+                       }
+               }
+               return edg_wll_SetError(ctx, intErr, NULL);
+       }
+
+}
+
+static int badEvent(intJobStat *js UNUSED_VAR, edg_wll_Event *e, int ev_seq UNUSED_VAR)
+{
+       char *str;
+
+       str = edg_wll_EventToString(e->any.type);
+       fprintf(stderr, "edg_wll_JobStatus:  bad event: type %d (%s)\n",
+               e->any.type, (str == NULL) ? "unknown" : str);
+       free(str);
+       return RET_FATAL;
+}
+
+#define USABLE(res,strict) ((res) == RET_OK || ( (res) == RET_SOON && !strict))
+#define USABLE_DATA(res,strict) ((res) == RET_OK || ( (res) != RET_FATAL && !strict))
+#define LRMS_STATE(state) ((state) == EDG_WLL_JOB_RUNNING || (state) == EDG_WLL_JOB_DONE)
+
+
+static int processEvent(intJobStat *js, edg_wll_Event *e, int ev_seq, int strict, char **errstring)
+{
+
+       edg_wll_JobStatCode     old_state = js->pub.state;
+       edg_wll_JobStatCode     new_state = EDG_WLL_JOB_UNKNOWN;
+       int             res = RET_OK;
+
+       if (old_state == EDG_WLL_JOB_ABORTED ||
+               old_state == EDG_WLL_JOB_CANCELLED ||
+               old_state == EDG_WLL_JOB_CLEARED) {
+               res = RET_LATE;
+       }
+
+       if (js->last_seqcode != NULL &&
+               edg_wll_compare_seq(e->any.seqcode, js->last_seqcode) < 0) {
+               res = RET_LATE;
+       }
+
+       switch (e->any.type) {
+               case EDG_WLL_EVENT_TRANSFER:
+                       if (e->transfer.result == EDG_WLL_TRANSFER_OK) {
+                               switch (e->transfer.source) {
+                                       case EDG_WLL_SOURCE_USER_INTERFACE:
+                                               new_state = EDG_WLL_JOB_WAITING; break;
+                                       case EDG_WLL_SOURCE_JOB_SUBMISSION:
+                                               /* if (LRMS_STATE(old_state)) res = RET_LATE; */
+                                               new_state = EDG_WLL_JOB_READY; break;
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               if (LRMS_STATE(old_state)) {
+                                                       js->pub.stateEnterTimes[1 + EDG_WLL_JOB_SCHEDULED] =
+                                                               e->any.timestamp.tv_sec;
+                                                       res = RET_LATE;
+                                               }
+                                               new_state = EDG_WLL_JOB_SCHEDULED; break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       } else if (e->transfer.result == EDG_WLL_TRANSFER_FAIL) {
+                               /* transfer failed */ 
+                               switch (e->transfer.source) {
+                                       case EDG_WLL_SOURCE_USER_INTERFACE:
+                                               new_state = EDG_WLL_JOB_SUBMITTED; break;
+                                       case EDG_WLL_SOURCE_JOB_SUBMISSION:
+                                               if (LRMS_STATE(old_state)) res = RET_LATE;
+                                               new_state = EDG_WLL_JOB_READY; break;
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               if (LRMS_STATE(old_state)) res = RET_LATE;
+                                               new_state = EDG_WLL_JOB_READY; break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       } else {
+                               /* e->transfer.result == EDG_WLL_TRANSFER_START */
+                               res = RET_IGNORE;
+                       }
+                       if (USABLE(res, strict)) {
+                               js->pub.state = new_state;
+                               rep(js->pub.reason, e->transfer.reason);
+
+                               free(js->pub.location);
+                               if (e->transfer.result == EDG_WLL_TRANSFER_OK) {
+                                       js->pub.location = location_string(
+                                               edg_wll_SourceToString(e->transfer.destination),
+                                               e->transfer.dest_host,
+                                               e->transfer.dest_instance);
+                               } else {
+                                       js->pub.location = location_string(
+                                               edg_wll_SourceToString(e->transfer.source),
+                                               e->transfer.host,
+                                               e->transfer.src_instance);
+                               }
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               switch (e->transfer.source) {
+                                       case EDG_WLL_SOURCE_USER_INTERFACE:
+                                               rep(js->pub.jdl, e->transfer.job); break;
+                                       case EDG_WLL_SOURCE_JOB_SUBMISSION:
+                                               rep(js->pub.condor_jdl, e->transfer.job); break;
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               rep(js->pub.rsl, e->transfer.job); break;
+                                       default:
+                                               goto bad_event; break;
+                                               
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_ACCEPTED:
+                       switch (e->accepted.source) {
+                               case EDG_WLL_SOURCE_NETWORK_SERVER:
+                                       new_state = EDG_WLL_JOB_WAITING; break;
+                               case EDG_WLL_SOURCE_LOG_MONITOR:
+                                       if (LRMS_STATE(old_state)) res = RET_LATE;
+                                       new_state = EDG_WLL_JOB_READY; break;
+                               case EDG_WLL_SOURCE_LRMS:
+                                       new_state = EDG_WLL_JOB_SCHEDULED; break;
+                               default:
+                                       goto bad_event; break;
+                       }
+                       if (USABLE(res, strict)) {
+                               js->pub.state = new_state;
+                               free(js->pub.location);
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(e->accepted.source),
+                                               e->accepted.host,
+                                               e->accepted.src_instance);
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               switch (e->accepted.source) {
+                                       case EDG_WLL_SOURCE_NETWORK_SERVER:
+                                               break; /* no WM id */
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               rep(js->pub.condorId, e->accepted.local_jobid); break;
+                                       case EDG_WLL_SOURCE_LRMS:
+                                               /* XXX localId */
+                                               rep(js->pub.globusId, e->accepted.local_jobid); break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_REFUSED:
+                       switch (e->refused.source) {
+                               case EDG_WLL_SOURCE_NETWORK_SERVER:
+                                       new_state = EDG_WLL_JOB_SUBMITTED; break;
+                               case EDG_WLL_SOURCE_LOG_MONITOR:
+                                       new_state = EDG_WLL_JOB_READY; break;
+                               case EDG_WLL_SOURCE_LRMS:
+                                       new_state = EDG_WLL_JOB_READY; break;
+                               default:
+                                       goto bad_event; break;
+                       }
+                       if (USABLE(res, strict)) {
+                               js->pub.state = new_state;
+                               rep(js->pub.reason, e->refused.reason);
+
+                               free(js->pub.location);
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(e->refused.from),
+                                               e->refused.from_host,
+                                               e->refused.from_instance);
+                       }
+                       break;
+               case EDG_WLL_EVENT_ENQUEUED:
+                       if (e->enQueued.result == EDG_WLL_ENQUEUED_OK) {
+                               switch (e->enQueued.source) {
+                                       case EDG_WLL_SOURCE_NETWORK_SERVER:
+                                               new_state = EDG_WLL_JOB_WAITING; break;
+                                       case EDG_WLL_SOURCE_WORKLOAD_MANAGER:
+                                               if (LRMS_STATE(old_state)) res = RET_LATE;
+                                               new_state = EDG_WLL_JOB_READY; break;
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               new_state = EDG_WLL_JOB_WAITING; break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       } else if (e->enQueued.result == EDG_WLL_ENQUEUED_FAIL) {
+                               switch (e->enQueued.source) {
+                                       case EDG_WLL_SOURCE_NETWORK_SERVER:
+                                               new_state = EDG_WLL_JOB_WAITING; break;
+                                       case EDG_WLL_SOURCE_WORKLOAD_MANAGER:
+                                               new_state = EDG_WLL_JOB_WAITING; break;
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               new_state = old_state; break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       } else { 
+                               /* e->enQueued.result == EDG_WLL_ENQUEUED_START */
+                               res = RET_IGNORE;
+                       }
+                       if (USABLE(res, strict)) {
+                               js->pub.state = new_state;
+                               rep(js->pub.reason, e->enQueued.reason);
+
+                               free(js->pub.location);
+                               if (e->transfer.result == EDG_WLL_ENQUEUED_OK) {
+                                       js->pub.location = location_string(
+                                               e->enQueued.queue,
+                                               e->enQueued.host,
+                                               e->enQueued.src_instance);
+                                       if (e->enQueued.source == EDG_WLL_SOURCE_LOG_MONITOR)
+                                               js->pub.resubmitted = 1;
+                               } else {
+                                       js->pub.location = location_string(
+                                               edg_wll_SourceToString(e->enQueued.source),
+                                               e->enQueued.host,
+                                               e->enQueued.src_instance);
+                               }
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               switch (e->enQueued.source) {
+                                       case EDG_WLL_SOURCE_NETWORK_SERVER:
+                                               rep(js->pub.jdl, e->enQueued.job); break;
+                                       case EDG_WLL_SOURCE_WORKLOAD_MANAGER:
+                                               rep(js->pub.matched_jdl,  e->enQueued.job); break;
+                                       case EDG_WLL_SOURCE_LOG_MONITOR:
+                                               /* no interim JDL here */
+                                               break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_DEQUEUED:
+                       switch (e->deQueued.source) {
+                               case EDG_WLL_SOURCE_WORKLOAD_MANAGER:
+                                       new_state = EDG_WLL_JOB_WAITING; break;
+                               case EDG_WLL_SOURCE_JOB_SUBMISSION:
+                                       if (LRMS_STATE(old_state)) res = RET_LATE;
+                                       new_state = EDG_WLL_JOB_READY; break;
+                               default:
+                                       goto bad_event; break;
+                       }
+                       if (USABLE(res, strict)) {
+                               js->pub.state = new_state;
+                               free(js->pub.location);
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(e->deQueued.source),
+                                               e->deQueued.host,
+                                               e->deQueued.src_instance);
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               /* no WM/JSS local jobid */
+                       }
+                       break;
+               case EDG_WLL_EVENT_HELPERCALL:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_WAITING;
+                               free(js->pub.location);
+                               js->pub.location = location_string(
+                                               e->helperCall.helper_name,
+                                               e->helperCall.host,
+                                               e->helperCall.src_instance);
+                               /* roles and params used only for debugging */
+                       }
+                       break;
+               case EDG_WLL_EVENT_HELPERRETURN:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_WAITING;
+                               free(js->pub.location);
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(EDG_WLL_SOURCE_WORKLOAD_MANAGER),
+                                               e->helperReturn.host,
+                                               e->helperReturn.src_instance);
+                               /* roles and retvals used only for debugging */
+                       }
+                       break;
+               case EDG_WLL_EVENT_RUNNING:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_RUNNING;
+                               free(js->pub.location);
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(EDG_WLL_SOURCE_LRMS),
+                                               "worknode",
+                                               e->running.node);
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               rep(js->pub.ce_node, e->running.node);
+                       }
+                       break;
+               case EDG_WLL_EVENT_RESUBMISSION:
+                       if (USABLE(res, strict)) {
+                               if (e->resubmission.result == EDG_WLL_RESUBMISSION_WONTRESUB) {
+                                       rep(js->pub.reason, e->resubmission.reason);
+                               }
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               if (e->resubmission.result == EDG_WLL_RESUBMISSION_WONTRESUB) {
+                                       js->wontresub = 1;
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_DONE:
+                       if (e->any.source == EDG_WLL_SOURCE_LRMS) {
+                               /* Done from JobWrapper is not sufficient for transition
+                                * to DONE state according its current definition */
+                               break;
+                       }
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_DONE;
+                               rep(js->pub.reason, e->done.reason);
+                               switch (e->done.status_code) {
+                                       case EDG_WLL_DONE_CANCELLED:
+                                               js->pub.state = EDG_WLL_JOB_CANCELLED;
+                                       case EDG_WLL_DONE_OK:
+                                               rep(js->pub.location, "none"); break;
+                                       default:
+                                               free(js->pub.location);
+                                               js->pub.location = location_string(
+                                                       edg_wll_SourceToString(e->done.source),
+                                                       e->done.host,
+                                                       e->done.src_instance);
+                               }
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               switch (e->done.status_code) {
+                                       case EDG_WLL_DONE_OK:
+                                               js->pub.exit_code = e->done.exit_code;
+                                               js->pub.done_code = EDG_WLL_STAT_OK; break;
+                                       case EDG_WLL_DONE_CANCELLED:
+                                               js->pub.exit_code = 0;
+                                               js->pub.done_code = EDG_WLL_STAT_CANCELLED; break;
+                                       case EDG_WLL_DONE_FAILED:
+                                               js->pub.exit_code = 0;
+                                               js->pub.done_code = EDG_WLL_STAT_FAILED; break;
+                                       default:
+                                               goto bad_event; break;
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_CANCEL:
+                       if (js->last_cancel_seqcode != NULL &&
+                               edg_wll_compare_seq(e->any.seqcode, js->last_cancel_seqcode) < 0) {
+                               res = RET_LATE;
+                       } 
+                       if (USABLE(res, strict)) {
+                               switch (e->cancel.status_code) {
+                                       case EDG_WLL_CANCEL_REQ:
+                                               js->pub.cancelling = 1; break;
+                                       case EDG_WLL_CANCEL_DONE:
+                                               js->pub.state = EDG_WLL_JOB_CANCELLED;
+                                               rep(js->pub.reason, e->cancel.reason);
+                                               rep(js->last_seqcode, e->any.seqcode);
+                                               rep(js->pub.location, "none");
+                                               /* fall though */
+                                       case EDG_WLL_CANCEL_ABORT:
+                                               js->pub.cancelling = 0; break;
+                                       default:
+                                               /* do nothing */
+                                               break;
+
+                               }
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               rep(js->pub.cancelReason, e->cancel.reason);
+                       }
+                       break;
+               case EDG_WLL_EVENT_ABORT:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_ABORTED;
+                               rep(js->pub.reason, e->abort.reason);
+                               rep(js->pub.location, "none");
+                       }
+                       break;
+
+               case EDG_WLL_EVENT_CLEAR:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_CLEARED;
+                               rep(js->pub.location, "none");
+                               switch (e->clear.reason) {
+                                       case EDG_WLL_CLEAR_USER:
+                                               rep(js->pub.reason, "user retrieved output sandbox");
+                                               break;
+                                       case EDG_WLL_CLEAR_TIMEOUT:
+                                               rep(js->pub.reason, "timed out, resource purge forced");
+                                               break;
+                                       case EDG_WLL_CLEAR_NOOUTPUT:
+                                               rep(js->pub.reason, "no output was generated");
+                                               break;
+                                       default:
+                                               goto bad_event; break;
+
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_PURGE:
+                       /* ignore, meta-information only */
+                       break;
+               case EDG_WLL_EVENT_MATCH:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_WAITING;
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(EDG_WLL_SOURCE_WORKLOAD_MANAGER),
+                                               e->match.host,
+                                               e->match.src_instance);
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               rep(js->pub.destination, e->match.dest_id);
+                       }
+                       break;
+               case EDG_WLL_EVENT_PENDING:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_WAITING;
+                               rep(js->pub.reason, e->pending.reason);
+                               js->pub.location = location_string(
+                                               edg_wll_SourceToString(EDG_WLL_SOURCE_WORKLOAD_MANAGER),
+                                               e->match.host,
+                                               e->match.src_instance);
+                       }
+                       break;
+               case EDG_WLL_EVENT_REGJOB:
+                       if (USABLE(res, strict)) {
+                               js->pub.state = EDG_WLL_JOB_SUBMITTED;
+                       }
+                       if (USABLE_DATA(res, strict)) {
+                               rep(js->pub.jdl, e->regJob.jdl);
+                               edg_wlc_JobIdFree(js->pub.parent_job);
+                               edg_wlc_JobIdDup(e->regJob.parent,
+                                                       &js->pub.parent_job);
+                               rep(js->pub.network_server, e->regJob.ns);
+                               js->pub.children_num = e->regJob.nsubjobs;
+                               if (e->regJob.jobtype == EDG_WLL_REGJOB_DAG
+                                       || e->regJob.jobtype == EDG_WLL_REGJOB_PARTITIONED) {
+                                       js->pub.jobtype = EDG_WLL_STAT_DAG;
+                               }
+                               rep(js->pub.seed, e->regJob.seed);
+                       }
+                       break;
+               case EDG_WLL_EVENT_USERTAG:
+                       if (USABLE_DATA(res, strict)) {
+                               if (e->userTag.name != NULL && e->userTag.value != NULL) {
+                                       add_taglist(&js->pub.user_tags, 
+                                                   e->userTag.name, e->userTag.value);
+                               } else {
+                                       goto bad_event;
+                               }
+                       }
+                       break;
+               case EDG_WLL_EVENT_LISTENER:
+                       /* ignore, listener port is not part of job status */
+                       break;
+               case EDG_WLL_EVENT_CURDESCR:
+               case EDG_WLL_EVENT_CHKPT:
+               case EDG_WLL_EVENT_CHANGEACL:
+                       /* ignore, only for event log */
+                       break;
+               
+               default:
+                       goto bad_event;
+                       break;
+       }
+
+       if (USABLE(res,strict)) {
+               js->pub.lastUpdateTime = e->any.timestamp;
+               if (old_state != js->pub.state) {
+                       js->pub.stateEnterTime = js->pub.lastUpdateTime;
+                       js->pub.stateEnterTimes[1 + js->pub.state]
+                               = (int)js->pub.lastUpdateTime.tv_sec;
+               }
+       }
+
+       if (USABLE_DATA(res,strict)) {
+               if (e->any.source == EDG_WLL_SOURCE_NETWORK_SERVER &&
+                       js->pub.network_server == NULL) {
+                       char *inst;
+                       inst = e->any.src_instance;
+                       asprintf(&js->pub.network_server, "%s%s%s",
+                               e->any.host,
+                               inst != NULL ? ":" : " ",
+                               inst != NULL ? inst : "");
+               }
+       }
+       
+       if (e->any.type == EDG_WLL_EVENT_CANCEL) {
+               rep(js->last_cancel_seqcode, e->any.seqcode);
+       } else {
+               rep(js->last_seqcode, e->any.seqcode);
+       }
+
+       return res;
+
+bad_event:
+       badEvent(js,e,ev_seq);
+       return RET_SUSPECT;
+}
+
+/*
+ * Helper for warning printouts
+ */
+
+static void warn(const char* format, ...)
+{
+       va_list l;
+       va_start(l, format);
+
+       /*
+       fprintf(stderr, "Warning: ");
+       vfprintf(stderr, format, l);
+       fputc('\n', stderr);
+       */
+
+       va_end(l);
+}
+
+static char *job_owner(edg_wll_Context ctx,char *md5_jobid)
+{
+       char    *stmt = NULL,*out = NULL;
+       edg_wll_Stmt    sh;
+       int     f = -1;
+       
+       edg_wll_ResetError(ctx);
+       trio_asprintf(&stmt,"select cert_subj from users,jobs "
+               "where users.userid = jobs.userid "
+               "and jobs.jobid = '%|Ss'",md5_jobid);
+
+       if (stmt==NULL) {
+               edg_wll_SetError(ctx,ENOMEM, NULL);
+               return NULL;
+       }
+       if (edg_wll_ExecStmt(ctx,stmt,&sh) >= 0) {
+               f=edg_wll_FetchRow(sh,&out);
+               if (f == 0) {
+                       if (out) free(out);
+                       out = NULL;
+                       edg_wll_SetError(ctx,ENOENT,md5_jobid);
+               }
+       }
+       edg_wll_FreeStmt(&sh);
+       free(stmt);
+
+       return out;
+}
+
+
+#if 0
+/* XXX went_through went out */
+static int eval_expect_update(intJobStat *js, int* went_through, char **expect_from)
+{
+       int em = 0;
+       int ft = 0; /* fall through following tests */
+
+       if (ft || (went_through[ EDG_WLL_JOB_OUTPUTREADY ] && !js->done_failed)) ft = 1;
+       if (ft || (went_through[ EDG_WLL_JOB_DONE ] && !js->done_failed)) ft = 1;
+       if (ft || went_through[ EDG_WLL_JOB_CHECKPOINTED ]) ft = 1;
+       if (ft || went_through[ EDG_WLL_JOB_RUNNING ]) {
+               if (js->pub.node == NULL) em |= EXPECT_MASK_JOBMGR;
+               ft = 1;
+       }
+       if (ft || went_through[ EDG_WLL_JOB_SCHEDULED ]) {
+                       if (js->pub.jssId == NULL) em |= EXPECT_MASK_JSS;
+                       if (js->pub.rsl == NULL) em |= EXPECT_MASK_JSS;
+                       if (js->pub.globusId == NULL) em |= EXPECT_MASK_JOBMGR;
+                       if (js->pub.localId == NULL) em |= EXPECT_MASK_JOBMGR;
+                       if (js->pub.jss_jdl == NULL) em |= EXPECT_MASK_RB;
+                       ft = 1;
+       }
+       if (ft || went_through[ EDG_WLL_JOB_READY ]) {
+               if (js->pub.destination == NULL) em |= EXPECT_MASK_RB;
+               ft = 1;
+       }
+       if (ft || went_through[ EDG_WLL_JOB_SUBMITTED ]) {
+               if (js->pub.jdl == NULL) em |= EXPECT_MASK_UI;
+               ft = 1;
+       }
+       
+       if (em == 0) 
+               *expect_from = NULL;
+       else {
+               asprintf(expect_from, "%s%s%s%s%s%s%s",
+                       (em & EXPECT_MASK_UI ) ? EDG_WLL_SOURCE_UI : "",
+                       (em & EXPECT_MASK_UI ) ? " " : "",
+                       (em & EXPECT_MASK_RB ) ? EDG_WLL_SOURCE_RB : "",
+                       (em & EXPECT_MASK_RB ) ? " " : "",
+                       (em & EXPECT_MASK_JSS ) ? EDG_WLL_SOURCE_JSS : "",
+                       (em & EXPECT_MASK_JSS ) ? " " : "",
+                       (em & EXPECT_MASK_JOBMGR ) ? EDG_WLL_SOURCE_JOBMGR : ""
+                       );
+       }
+
+       return (em == 0) ? 0 : 1;
+}
+#endif
+
+static char* location_string(const char *source, const char *host, const char *instance)
+{
+       char *ret;
+       asprintf(&ret, "%s/%s/%s", source, host, instance);
+       return ret;
+}
+
+static int add_stringlist(char ***lptr, const char *new_item)
+{
+       char **itptr;
+       int             i;
+
+       if (*lptr == NULL) {
+               itptr = (char **) malloc(2*sizeof(char *));
+               itptr[0] = strdup(new_item);
+               itptr[1] = NULL;
+               *lptr = itptr;
+               return 1;
+       } else {
+               for (i = 0, itptr = *lptr; itptr[i] != NULL; i++);
+               itptr = (char **) realloc(*lptr, (i+2)*sizeof(char *));
+               if (itptr != NULL) {
+                       itptr[i] = strdup(new_item);
+                       itptr[i+1] = NULL;
+                       *lptr = itptr;
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
+}
+
+static int add_taglist(edg_wll_TagValue **lptr, const char *new_item, const char *new_item2)
+{
+       edg_wll_TagValue        *itptr;
+       int                     i;
+
+       if (*lptr == NULL) {
+               itptr = (edg_wll_TagValue *) calloc(2,sizeof(edg_wll_TagValue));
+               itptr[0].tag = strdup(new_item);
+               itptr[0].value = strdup(new_item2);
+               *lptr = itptr;
+               return 1;
+       } else {
+               for (i = 0, itptr = *lptr; itptr[i].tag != NULL; i++)
+                       if ( !strcasecmp(itptr[i].tag, new_item) )
+                       {
+                               free(itptr[i].value);
+                               itptr[i].value = strdup(new_item2);
+                               return 1;
+                       }
+               itptr = (edg_wll_TagValue *) realloc(*lptr, (i+2)*sizeof(edg_wll_TagValue));
+               if (itptr != NULL) {
+                       itptr[i].tag = strdup(new_item);
+                       itptr[i].value = strdup(new_item2);
+                       itptr[i+1].tag = NULL;
+                       itptr[i+1].value = NULL;
+                       *lptr = itptr;
+                       return 1;
+               } else {
+                       return 0;
+               }
+       }
+}
+
+/* XXX more thorough malloc, calloc, and asprintf failure handling */
+/* XXX indexes in {short,long}_fields */
+/* XXX strict mode */
+/* XXX caching */
+
+/*
+ * Store current job state to states and status_tags DB tables.
+ * Should be called with the job locked.
+ */
+
+edg_wll_ErrorCode edg_wll_StoreIntState(edg_wll_Context ctx,
+                                    intJobStat *stat,
+                                    int seq)
+{
+       char *jobid_md5, *stat_enc, *parent_md5 = NULL;
+       char *stmt;
+       edg_wll_TagValue *tagp;
+       int update;
+       int dbret;
+       char *icnames, *icvalues;
+
+       update = (seq > 0);
+       jobid_md5 = edg_wlc_JobIdGetUnique(stat->pub.jobId);
+       stat_enc = enc_intJobStat(strdup(""), stat);
+
+       tagp = stat->pub.user_tags;
+       if (tagp) {
+               while ((*tagp).tag != NULL) {
+                       trio_asprintf(&stmt, "insert into status_tags"
+                                       "(jobid,seq,name,value) values "
+                                       "('%|Ss',%d,'%|Ss','%|Ss')",
+                                       jobid_md5, seq, (*tagp).tag, (*tagp).value);
+                       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) {
+                               if (EEXIST == edg_wll_Error(ctx, NULL, NULL)) {
+                               /* XXX: this should not happen */
+                                       edg_wll_ResetError(ctx);
+                                       tagp++;
+                                       continue;
+                               }
+                               else
+                                       goto cleanup;
+                       }
+                       tagp++;
+               }
+       }
+
+       parent_md5 = edg_wlc_JobIdGetUnique(stat->pub.parent_job);
+       if (parent_md5 == NULL) parent_md5 = strdup("*no parent job*");
+
+
+       edg_wll_IColumnsSQLPart(ctx, ctx->job_index_cols, stat, 0, NULL, &icvalues);
+
+       trio_asprintf(&stmt,
+               "update states set "
+               "status=%d,seq=%d,int_status='%|Ss',version='%|Ss'"
+                       ",parent_job='%|Ss'%s "
+               "where jobid='%|Ss'",
+               stat->pub.state, seq, stat_enc, INTSTAT_VERSION,
+               parent_md5, icvalues,
+               jobid_md5);
+       free(icvalues);
+
+       if ((dbret = edg_wll_ExecStmt(ctx,stmt,NULL)) < 0) goto cleanup;
+
+       if (dbret == 0) {
+               edg_wll_IColumnsSQLPart(ctx, ctx->job_index_cols, stat, 1, &icnames, &icvalues);
+               trio_asprintf(&stmt,
+                       "insert into states"
+                       "(jobid,status,seq,int_status,version"
+                               ",parent_job%s) "
+                       "values ('%|Ss',%d,%d,'%|Ss','%|Ss','%|Ss'%s)",
+                       icnames,
+                       jobid_md5, stat->pub.state, seq, stat_enc,
+                       INTSTAT_VERSION, parent_md5, icvalues);
+               free(icnames); free(icvalues);
+
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) goto cleanup;
+       }
+
+       if (update) {
+               trio_asprintf(&stmt, "delete from states "
+                       "where jobid ='%|Ss' and ( seq<%d or version !='%|Ss')",
+                       jobid_md5, seq, INTSTAT_VERSION);
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) goto cleanup;
+       }
+       if (update) {
+               trio_asprintf(&stmt, "delete from status_tags "
+                       "where jobid ='%|Ss' and seq<%d", jobid_md5, seq);
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) goto cleanup;
+       }
+
+       if (ctx->rgma_export) write2rgma_status(&stat->pub);
+
+cleanup:
+       free(stmt); 
+       free(jobid_md5); free(stat_enc);
+       free(parent_md5);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+/*
+ * Retrieve stored job state from states and status_tags DB tables.
+ * Should be called with the job locked.
+ */
+
+edg_wll_ErrorCode edg_wll_LoadIntState(edg_wll_Context ctx,
+                                   edg_wlc_JobId jobid,
+                                   int seq,
+                                   intJobStat **stat)
+{
+       char *jobid_md5;
+       char *stmt;
+       edg_wll_Stmt sh;
+       char *res, *res_rest;
+       int nstates;
+
+       edg_wll_ResetError(ctx);
+       jobid_md5 = edg_wlc_JobIdGetUnique(jobid);
+
+       if (seq == -1) {
+               /* any sequence number */
+               trio_asprintf(&stmt,
+                       "select int_status from states "
+                       "where jobid='%|Ss' and version='%|Ss'",
+                       jobid_md5, INTSTAT_VERSION);
+       } else {
+               trio_asprintf(&stmt,
+                       "select int_status from states "
+                       "where jobid='%|Ss' and seq='%d' and version='%|Ss'",
+                       jobid_md5, seq, INTSTAT_VERSION);
+       }
+
+       if (stmt == NULL) {
+               return edg_wll_SetError(ctx, ENOMEM, NULL);
+       }
+
+       if ((nstates = edg_wll_ExecStmt(ctx,stmt,&sh)) < 0) goto cleanup;
+       if (nstates == 0) {
+               edg_wll_SetError(ctx,ENOENT,"no state in DB");
+               goto cleanup;
+       }
+       if (edg_wll_FetchRow(sh,&res) < 0) goto cleanup;
+
+       *stat = dec_intJobStat(res, &res_rest);
+       if (res_rest == NULL) {
+               edg_wll_SetError(ctx, EDG_WLL_ERROR_DB_CALL,
+                               "error decoding DB intJobStatus");
+       }
+
+       free(res);
+cleanup:
+       free(jobid_md5);
+       free(stmt); edg_wll_FreeStmt(&sh);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+/*
+ * update stored state according to the new event
+ * (must be called with the job locked)
+ */
+
+edg_wll_ErrorCode edg_wll_StepIntState(edg_wll_Context ctx,
+                                       edg_wlc_JobId job,
+                                       edg_wll_Event *e,
+                                       int seq,
+                                       edg_wll_JobStat *stat_out)
+{
+       intJobStat      *ijsp;
+       int             intErr = 0;
+       int             flags = 0;
+       int             res;
+       int             be_strict = 0;
+       char            *errstring = NULL;
+       intJobStat      jobstat;
+
+       if (seq != 0) {
+               intErr = edg_wll_LoadIntState(ctx, job, seq - 1, &ijsp);
+       }
+       if (seq != 0 && !intErr) {
+               res = processEvent(ijsp, e, seq, be_strict, &errstring);
+               if (res == RET_FATAL || res == RET_INTERNAL) { /* !strict */
+                       return edg_wll_SetError(ctx, EINVAL, errstring);
+               }
+               edg_wll_StoreIntState(ctx, ijsp, seq);
+               if (stat_out) {
+                       memcpy(stat_out,&ijsp->pub,sizeof *stat_out);
+                       destroy_intJobStat_extension(ijsp);
+               }
+               else destroy_intJobStat(ijsp);
+               free(ijsp);
+       } else {
+               edg_wll_intJobStatus(ctx, job, flags,&jobstat, js_enable_store);
+               if (stat_out) {
+                       memcpy(stat_out,&jobstat.pub,sizeof *stat_out);
+                       destroy_intJobStat_extension(&jobstat);
+               }
+               else destroy_intJobStat(&jobstat);
+       }
+       return edg_wll_Error(ctx, NULL, NULL);
+}
diff --git a/org.glite.lb.server/src/jobstat.h b/org.glite.lb.server/src/jobstat.h
new file mode 100644 (file)
index 0000000..1b17c7b
--- /dev/null
@@ -0,0 +1,30 @@
+/* $Header$ */
+
+/*
+ * Internal representation of job state
+ * (includes edg_wll_JobStat API structure)
+ */
+
+#define INTSTAT_VERSION "release-2.0"
+
+
+typedef struct _intJobStat {
+               edg_wll_JobStat pub;
+               int             wontresub;
+               char            *last_seqcode;
+               char            *last_cancel_seqcode;
+
+/*             int             expect_mask; */
+       } intJobStat;
+
+void destroy_intJobStat(intJobStat *);
+
+edg_wll_ErrorCode edg_wll_IColumnsSQLPart(edg_wll_Context, void *, intJobStat *, int , char **, char **);
+edg_wll_ErrorCode edg_wll_RefreshIColumns(edg_wll_Context, void *);
+int edg_wll_intJobStatus( edg_wll_Context, const edg_wlc_JobId, int, intJobStat *, int);
+
+intJobStat* dec_intJobStat(char *, char **);
+char *enc_intJobStat(char *, intJobStat* );
+
+void write2rgma_status(edg_wll_JobStat *);
+
diff --git a/org.glite.lb.server/src/jobstat_supp.c b/org.glite.lb.server/src/jobstat_supp.c
new file mode 100644 (file)
index 0000000..5a6f97e
--- /dev/null
@@ -0,0 +1,669 @@
+#ident "$Header$"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <regex.h>
+#include <syslog.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+
+#include "store.h"
+#include "index.h"
+#include "jobstat.h"
+#include "lbs_db.h"
+#include "get_events.h"
+
+
+extern int debug;
+#ifndef dprintf
+#define dprintf(x) { if (debug) printf x; }
+#endif
+
+
+/* TBD: share in whole logging or workload */
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+static char* enc_strlist(char *, char **) UNUSED_VAR;
+static char **dec_strlist(char *, char **) UNUSED_VAR;
+
+/*
+ * string encoding routines for safe DB store
+ */
+
+static char *enc_string(char *old, char *item)
+{
+       char *out;
+       if (item == NULL) {
+               asprintf(&out,"%s-1 ", old);
+       } else {
+               asprintf(&out,"%s%d %s",old, strlen(item), item);
+       }
+       free(old);
+       return out;
+}
+
+static char *dec_string(char *in, char **rest)
+{
+       int scret;
+       int len = -1;
+       char *out;
+       
+       scret = sscanf(in, "%d", &len);
+       if (scret < 1) {
+               *rest = NULL;
+               return NULL;
+       }
+       if (len == -1) {
+               out = NULL;
+               *rest = strchr(in, ' ') ? strchr(in, ' ') + 1 : NULL;
+       } else {
+               in = strchr(in, ' ') ? strchr(in, ' ') + 1 : NULL;
+               out = (char *)malloc(len+1);
+               if (out) {
+                       memcpy(out, in, len);
+                       *(out+len) = '\0';
+               }
+               *rest = in+len;
+       }
+       return out;
+}
+
+static char *enc_int(char *old, int item)
+{
+        char *out;
+        asprintf(&out,"%s%d ", old, item);
+       free(old);
+        return out;
+}
+
+static int dec_int(char* in, char **rest)
+{
+       int scret;
+       int out;
+
+       scret = sscanf(in, "%d", &out);
+       if (scret == 1) {
+               *rest = strchr(in, ' ') ? strchr(in, ' ') + 1 : NULL;
+       } else {
+               out = 0;
+               *rest = in;
+       }
+       return out;
+}
+
+static char* enc_jobid(char *old, edg_wlc_JobId item)
+{
+       char *str;
+       char *out;
+
+       str = edg_wlc_JobIdUnparse(item);
+       out = enc_string(old, str);
+       free(str);
+       return out;
+}
+static edg_wlc_JobId dec_jobid(char *in, char **rest)
+{
+       char *str;
+       edg_wlc_JobId jobid;
+       
+       str = dec_string(in, rest);
+       if (str == NULL) return NULL;
+       edg_wlc_JobIdParse(str, &jobid);
+       free(str);
+       return jobid;
+}
+
+static char* enc_strlist(char *old, char **item)
+{
+       char *ret;
+
+       if (item == NULL) {
+               asprintf(&ret,"%s-1 ", old);
+               free(old);
+               return ret;
+       } else {
+               asprintf(&ret,"%s1 ",old);
+               free(old);
+               if (ret == NULL) return ret;
+       }
+       do {
+               ret = enc_string(ret, *item);
+       } while (*(item++) != NULL);
+       return ret;
+}
+
+static char **dec_strlist(char *in, char **rest)
+{
+       char **out;
+       int len = -1;
+       char *tmp_in, *tmp_ret;
+       int scret;
+
+       scret = sscanf(in, "%d", &len);
+       if (scret < 1) {
+               *rest = NULL;
+               return NULL;
+       }
+       if (len == -1) {
+               *rest = strchr(in, ' ') ? strchr(in, ' ') + 1 : NULL;
+               return NULL;
+       }
+
+       len = 0;
+       tmp_in = in = strchr(in, ' ') + 1 ;
+       do {
+               tmp_ret = dec_string(tmp_in, &tmp_in);
+               len++;
+       }  while (tmp_ret != NULL);
+
+       out = (char**) malloc(len*sizeof(char*));
+
+       if (out) {
+               len = 0;
+               tmp_in = in;
+               do {
+                       out[len] = dec_string(tmp_in, &tmp_in);
+               } while  (out[len++] != NULL);
+       }
+       *rest = tmp_in;
+       return out;
+}
+
+static char* enc_taglist(char *old, edg_wll_TagValue *item)
+{
+       char *ret;
+
+       if (item == NULL) {
+               asprintf(&ret,"%s-1 ", old);
+               free(old);
+               return ret;
+       } else {
+               asprintf(&ret,"%s1 ",old);
+               free(old);
+               if (ret == NULL) return ret;
+       }
+       do {
+               ret = enc_string(ret, (*item).tag);
+               ret = enc_string(ret, (*item).value);
+       } while ((*(item++)).tag != NULL);
+       return ret;
+}
+
+static edg_wll_TagValue *dec_taglist(char *in, char **rest)
+{
+       edg_wll_TagValue *out;
+       int len = -1;
+       char *tmp_in, *tmp_ret, *tmp_ret2;
+       int scret;
+
+       scret = sscanf(in, "%d", &len);
+       if (scret < 1) {
+               *rest = NULL;
+               return NULL;
+       }
+       if (len == -1) {
+               *rest = strchr(in, ' ') ? strchr(in, ' ') + 1 : NULL;
+               return NULL;
+       }
+
+       len = 0;
+       tmp_in = in = strchr(in, ' ') + 1 ;
+       do {
+               tmp_ret2 = dec_string(tmp_in, &tmp_in);
+               if (!tmp_in) { *rest = tmp_in; return NULL; }
+               tmp_ret = dec_string(tmp_in, &tmp_in);
+               if (!tmp_in) { *rest = tmp_in; return NULL; }
+               len++;
+       }  while (tmp_ret2 != NULL);
+
+       out = (edg_wll_TagValue *) malloc(len*sizeof(edg_wll_TagValue));
+
+       if (out) {
+               len = 0;
+               tmp_in = in;
+
+               do {
+                       out[len].tag = dec_string(tmp_in, &tmp_in);
+                       out[len].value = dec_string(tmp_in, &tmp_in);
+               } while  (out[len++].tag != NULL);
+               *rest = tmp_in;
+       }
+       else 
+               *rest = 0;
+
+       return out;
+}
+
+static char *enc_intlist(char *old, int *item)
+{
+       int len;
+       char *ret;
+
+       if (item == NULL) {
+               asprintf(&ret,"%s-1 ", old);
+               free(old);
+               return ret;
+       } else {
+               asprintf(&ret,"%s1 ",old);
+               free(old);
+               if (ret == NULL) return ret;
+       }
+       len = *item; item++;
+       ret = enc_int(ret, len);
+       for (; len > 0 ; len--, item++) {
+               ret = enc_int(ret, *item);
+       }
+
+       return ret;
+}
+
+static int *dec_intlist(char *in, char **rest)
+{
+       int len = -1;
+       int *out, *ptr;
+       char *tmp_in;
+       int scret;
+
+       scret = sscanf(in, "%d", &len);
+       if (scret < 1) {
+               *rest = NULL;
+               return NULL;
+       }
+       tmp_in = strchr(in, ' ') ? strchr(in, ' ') + 1 : NULL;
+       if (len == -1 || tmp_in == NULL) {
+               *rest = tmp_in;
+               return NULL;
+       }
+
+       len = dec_int(tmp_in, &tmp_in);
+       out = (int *)malloc( (len+1) *sizeof(int));
+       if (out) {
+               *out = len; 
+               ptr = out+1;
+               while (len) {
+                       *ptr = dec_int(tmp_in, &tmp_in);
+                       len--; ptr++;
+               }
+       }
+       *rest = tmp_in;
+       return out;
+}
+
+static char* enc_timeval(char *old, struct timeval item)
+{
+       char *ret;
+       
+       ret = enc_int(old, (int)item.tv_sec);
+       if (ret) {
+               ret = enc_int(ret, (int)item.tv_usec);
+       }
+       return ret;
+}
+
+static struct timeval dec_timeval(char *in, char **rest)
+{
+       struct timeval t;
+       char *tmp_in;
+       
+       t.tv_sec = dec_int(in, &tmp_in);
+       if (tmp_in != NULL) t.tv_usec = dec_int(tmp_in, &tmp_in);
+       *rest = tmp_in;
+       return t;
+}
+
+static char *enc_JobStat(char *old, edg_wll_JobStat* stat)
+{
+       char *ret;
+
+       ret = enc_int(old, stat->state);
+       if (ret) ret = enc_jobid(ret, stat->jobId);
+       if (ret) ret = enc_string(ret, stat->owner);
+       if (ret) ret = enc_int(ret, stat->jobtype);
+       if (ret) ret = enc_jobid(ret, stat->parent_job);
+       if (ret) ret = enc_string(ret, stat->seed);
+       if (ret) ret = enc_int(ret, stat->children_num);
+               /* children data are not stored in DB */
+       if (ret) ret = enc_string(ret, stat->condorId);
+       if (ret) ret = enc_string(ret, stat->globusId);
+       if (ret) ret = enc_string(ret, stat->localId);
+       if (ret) ret = enc_string(ret, stat->jdl);
+       if (ret) ret = enc_string(ret, stat->matched_jdl);
+       if (ret) ret = enc_string(ret, stat->destination);
+       if (ret) ret = enc_string(ret, stat->condor_jdl);
+       if (ret) ret = enc_string(ret, stat->rsl);
+       if (ret) ret = enc_string(ret, stat->reason);
+       if (ret) ret = enc_string(ret, stat->location);
+       if (ret) ret = enc_string(ret, stat->ce_node);
+       if (ret) ret = enc_string(ret, stat->network_server);
+       if (ret) ret = enc_int(ret, stat->subjob_failed);
+       if (ret) ret = enc_int(ret, stat->done_code);
+       if (ret) ret = enc_int(ret, stat->exit_code);
+       if (ret) ret = enc_int(ret, stat->resubmitted);
+       if (ret) ret = enc_int(ret, stat->cancelling);
+       if (ret) ret = enc_string(ret, stat->cancelReason);
+       if (ret) ret = enc_int(ret, stat->cpuTime);
+       if (ret) ret = enc_taglist(ret, stat->user_tags);
+       if (ret) ret = enc_timeval(ret, stat->stateEnterTime);
+       if (ret) ret = enc_intlist(ret, stat->stateEnterTimes);
+       if (ret) ret = enc_timeval(ret, stat->lastUpdateTime);
+       if (ret) ret = enc_int(ret, stat->expectUpdate);
+       if (ret) ret = enc_string(ret, stat->expectFrom);
+
+       return ret;
+}
+static edg_wll_JobStat* dec_JobStat(char *in, char **rest)
+{
+       char *tmp_in;
+       edg_wll_JobStat *stat;
+
+       stat = (edg_wll_JobStat *) calloc(1,sizeof(edg_wll_JobStat));
+       if (!stat) return stat;
+
+       tmp_in = in;
+        stat->state = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->jobId = dec_jobid(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->owner = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->jobtype = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->parent_job = dec_jobid(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->seed = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->children_num = dec_int(tmp_in, &tmp_in);
+                /* children data are not stored in DB */
+        if (tmp_in != NULL) stat->condorId = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->globusId = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->localId = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->jdl = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->matched_jdl = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->destination = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->condor_jdl = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->rsl = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->reason = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->location = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->ce_node = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->network_server = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->subjob_failed = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->done_code = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->exit_code = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->resubmitted = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->cancelling = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->cancelReason = dec_string(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->cpuTime = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->user_tags = dec_taglist(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->stateEnterTime = dec_timeval(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->stateEnterTimes = dec_intlist(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->lastUpdateTime = dec_timeval(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->expectUpdate = dec_int(tmp_in, &tmp_in);
+        if (tmp_in != NULL) stat->expectFrom = dec_string(tmp_in, &tmp_in);
+       
+       *rest = tmp_in;
+       return stat;
+}
+
+char *enc_intJobStat(char *old, intJobStat* stat)
+{
+       char *ret;
+
+       ret = enc_JobStat(old, &stat->pub);
+       if (ret) ret = enc_int(ret, stat->wontresub);
+       if (ret) ret = enc_string(ret, stat->last_seqcode);
+       if (ret) ret = enc_string(ret, stat->last_cancel_seqcode);
+       return ret;
+}
+
+intJobStat* dec_intJobStat(char *in, char **rest)
+{
+       edg_wll_JobStat *pubstat;
+       intJobStat *stat = 0;
+       char *tmp_in;
+
+       pubstat = dec_JobStat(in, &tmp_in);
+       if (tmp_in != NULL) {
+               stat = (intJobStat *)calloc(1,sizeof(intJobStat));
+       } 
+       if (stat != NULL) {
+               stat->pub = *pubstat;
+               free(pubstat);
+               stat->wontresub = dec_int(tmp_in, &tmp_in);
+               if (tmp_in != NULL) {
+                       stat->last_seqcode = dec_string(tmp_in, &tmp_in);
+               }
+               if (tmp_in != NULL) {
+                       stat->last_cancel_seqcode = dec_string(tmp_in, &tmp_in);
+               }
+       } else if (tmp_in != NULL) {
+               edg_wll_FreeStatus(pubstat);
+               free(pubstat);
+       }
+
+       *rest = tmp_in;
+       return stat;
+}
+
+/*
+ * Compute part of SQL command used for indexed state table columns
+ */
+
+edg_wll_ErrorCode edg_wll_IColumnsSQLPart(edg_wll_Context ctx,
+                                       void *job_index_cols_v,
+                                       intJobStat *stat,
+                                       int is_insert,
+                                       char **names_out,
+                                       char **values_out)
+{
+       int i;
+       char *names, *values;
+       char *data;
+       char *tmp;
+       edg_wll_IColumnRec *job_index_cols = (edg_wll_IColumnRec *)job_index_cols_v;
+
+       edg_wll_ResetError(ctx);
+
+       if (is_insert) names = strdup(""); else names = NULL;
+       values = strdup("");
+
+       if (job_index_cols != NULL)
+       for (i=0; job_index_cols[i].colname; i++) {
+               data = NULL;
+               switch (job_index_cols[i].qrec.attr) {
+                       case EDG_WLL_QUERY_ATTR_OWNER:
+                               if (stat->pub.owner)
+                                       trio_asprintf(&data, "'%|Ss'", stat->pub.owner);
+                               else data = strdup("''");
+                               break;
+                       case EDG_WLL_QUERY_ATTR_LOCATION:
+                               if (stat->pub.location)
+                                       trio_asprintf(&data, "'%|Ss'", stat->pub.location);
+                               else data = strdup("''");
+                               break;
+                       case EDG_WLL_QUERY_ATTR_DESTINATION:
+                               if (stat->pub.destination)
+                                       trio_asprintf(&data, "'%|Ss'", stat->pub.destination);
+                               else data = strdup("''");
+                               break;
+                       case EDG_WLL_QUERY_ATTR_DONECODE:
+                               asprintf(&data, "%d", stat->pub.done_code);
+                               break;
+                       case EDG_WLL_QUERY_ATTR_USERTAG:
+                               if (stat->pub.user_tags) {
+                                       int k;
+                                       for (k=0; stat->pub.user_tags[k].tag &&
+                                                       strcmp(stat->pub.user_tags[k].tag,job_index_cols[i].qrec.attr_id.tag);
+                                               k++);
+                                       if (stat->pub.user_tags[k].tag != NULL) {
+                                               trio_asprintf(&data, "'%|Ss'", stat->pub.user_tags[k].value);
+                                       } else data = strdup("''");
+                               } else data = strdup("''");
+                               break;
+                       case EDG_WLL_QUERY_ATTR_TIME:
+                               if (stat->pub.stateEnterTimes)
+                                       data = strdup(edg_wll_TimeToDB(stat->pub.stateEnterTimes[
+                                                       job_index_cols[i].qrec.attr_id.state+1]));
+                               else data = strdup("0");
+                               break;
+                       case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                               asprintf(&data, "%d", stat->pub.resubmitted);
+                               break;
+
+                               /* XXX add more attributes when defined */
+                       default:
+                               /* do not use */
+                               break;
+               }
+
+               if (!data) continue;
+
+               if (is_insert) {
+                       asprintf(&tmp, "%s,`%s`", names, job_index_cols[i].colname);
+                       free(names); names = tmp;
+                       asprintf(&tmp, "%s,%s", values, data);
+                       free(values); values = tmp;
+               } else {
+                       /* update */
+                       asprintf(&tmp, "%s,`%s`=%s", values, job_index_cols[i].colname, data);
+                       free(values); values = tmp;
+               }
+               free(data);
+       }
+
+       if (is_insert) *names_out = names;
+       *values_out = values;
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+
+/*
+ * Set values of index columns in state table (after index reconfiguration)
+ */
+
+edg_wll_ErrorCode edg_wll_RefreshIColumns(edg_wll_Context ctx, void *job_index_cols) {
+
+       edg_wll_Stmt sh, sh2;
+       int njobs, ret = -1;
+       intJobStat *stat;
+       edg_wlc_JobId jobid;
+       char *res[5];
+       char *rest;
+       char *icvalues, *stmt;
+       int i;
+
+       edg_wll_ResetError(ctx);
+       if (!job_index_cols) return 0;
+
+       if ((njobs = edg_wll_ExecStmt(ctx, "select s.jobid,s.int_status,s.seq,s.version,j.dg_jobid"
+                                      " from states s, jobs j where s.jobid=j.jobid",&sh)) < 0) {
+               edg_wll_FreeStmt(&sh);
+               return edg_wll_Error(ctx, NULL, NULL);
+       }
+       while ((ret=edg_wll_FetchRow(sh,res)) >0) {
+               if (strcmp(res[3], INTSTAT_VERSION)) {
+                       stat = NULL;
+                       if (!edg_wlc_JobIdParse(res[4], &jobid)) {
+                               if ((stat = malloc(sizeof(intJobStat))) != NULL) {
+                                       if (edg_wll_intJobStatus(ctx, jobid, 0, stat, 1)) {
+                                               free(stat);
+                                               stat = NULL;
+                                       }
+                               }
+                               edg_wlc_JobIdFree(jobid);
+                       }
+               } else {
+                       stat = dec_intJobStat(res[1], &rest);
+                       if (rest == NULL) stat = NULL;
+               }
+               if (stat == NULL) {
+                       edg_wll_FreeStmt(&sh);
+                       return edg_wll_SetError(ctx, EDG_WLL_ERROR_SERVER_RESPONSE,
+                               "cannot decode int_status from states DB table");
+               }
+
+               edg_wll_IColumnsSQLPart(ctx, job_index_cols, stat, 0, NULL, &icvalues);
+               trio_asprintf(&stmt, "update states set seq=%s%s where jobid='%|Ss'", res[2], icvalues, res[0]);
+               ret = edg_wll_ExecStmt(ctx, stmt, &sh2);
+               edg_wll_FreeStmt(&sh2);
+
+               for (i = 0; i < 5; i++) free(res[i]);
+               destroy_intJobStat(stat); free(stat);
+               free(stmt); free(icvalues);
+
+               if (ret < 0) return edg_wll_Error(ctx, NULL, NULL);
+               
+       }
+       edg_wll_FreeStmt(&sh);
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+
+int edg_wll_compare_seq(const char *a, const char *b)
+{
+       unsigned int    c[EDG_WLL_SOURCE__LAST];
+       unsigned int    d[EDG_WLL_SOURCE__LAST];
+       int             res, i;
+
+       assert(EDG_WLL_SOURCE__LAST == 9);
+
+       res =  sscanf(a, "UI=%d:NS=%d:WM=%d:BH=%d:JSS=%d:LM=%d:LRMS=%d:APP=%d",
+                       &c[EDG_WLL_SOURCE_USER_INTERFACE],
+                       &c[EDG_WLL_SOURCE_NETWORK_SERVER],
+                       &c[EDG_WLL_SOURCE_WORKLOAD_MANAGER],
+                       &c[EDG_WLL_SOURCE_BIG_HELPER],
+                       &c[EDG_WLL_SOURCE_JOB_SUBMISSION],
+                       &c[EDG_WLL_SOURCE_LOG_MONITOR],
+                       &c[EDG_WLL_SOURCE_LRMS],
+                       &c[EDG_WLL_SOURCE_APPLICATION]);
+       if (res != EDG_WLL_SOURCE__LAST-1) {
+               syslog(LOG_ERR, "unparsable sequence code %s\n", a);
+               dprintf(( "unparsable sequence code %s\n", a));
+               return -1;
+       }
+
+       res =  sscanf(b, "UI=%d:NS=%d:WM=%d:BH=%d:JSS=%d:LM=%d:LRMS=%d:APP=%d",
+                       &d[EDG_WLL_SOURCE_USER_INTERFACE],
+                       &d[EDG_WLL_SOURCE_NETWORK_SERVER],
+                       &d[EDG_WLL_SOURCE_WORKLOAD_MANAGER],
+                       &d[EDG_WLL_SOURCE_BIG_HELPER],
+                       &d[EDG_WLL_SOURCE_JOB_SUBMISSION],
+                       &d[EDG_WLL_SOURCE_LOG_MONITOR],
+                       &d[EDG_WLL_SOURCE_LRMS],
+                       &d[EDG_WLL_SOURCE_APPLICATION]);
+       if (res != EDG_WLL_SOURCE__LAST-1) {
+               syslog(LOG_ERR, "unparsable sequence code %s\n", b);
+               dprintf(( "unparsable sequence code %s\n", b));
+               return 1;
+       }
+
+       for (i = EDG_WLL_SOURCE_USER_INTERFACE ; i < EDG_WLL_SOURCE__LAST; i++) {
+               if (c[i] < d[i]) return -1;
+               if (c[i] > d[i]) return  1;
+       }
+
+       return 0;
+}
+
+static int compare_events_by_seq(const void *a, const void *b)
+{
+        const edg_wll_Event *e = (edg_wll_Event *)a;
+        const edg_wll_Event *f = (edg_wll_Event *)b;
+
+       return edg_wll_compare_seq(e->any.seqcode, f->any.seqcode);
+}
+
+void edg_wll_SortEvents(edg_wll_Event *e)
+{
+       int     n;
+
+       if (!e) return;
+       for (n=0; e[n].type; n++);
+       qsort(e,n,sizeof *e,compare_events_by_seq);
+}
diff --git a/org.glite.lb.server/src/lb_authz.c b/org.glite.lb.server/src/lb_authz.c
new file mode 100644 (file)
index 0000000..3f4c39b
--- /dev/null
@@ -0,0 +1,765 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <voms_apic.h>
+#include <openssl/ssl.h>
+#include <libxml/parser.h> 
+#undef WITHOUT_TRIO
+
+#include "glite/wms/jobid/strmd5.h"
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/trio.h"
+#include "lb_authz.h"
+#include "lbs_db.h"
+
+GACLentry *GACLparseEntry(xmlNodePtr cur);
+
+static int
+add_groups(edg_wll_Context ctx, struct voms *voms_cert, char *vo_name,
+          edg_wll_VomsGroups *groups)
+{
+   struct data **voms_data;
+   edg_wll_VomsGroup *tmp = NULL;
+
+   if (voms_cert->type != TYPE_STD) {
+      edg_wll_SetError(ctx, EINVAL, "not supported VOMS certificate type");
+      return EINVAL;
+   }
+
+   for (voms_data = voms_cert->std; voms_data && *voms_data; voms_data++) {
+      if ((*voms_data)->group && *(*voms_data)->group) {
+        tmp = realloc(groups->val, (groups->len + 1) * sizeof(*groups->val));
+        if (tmp == NULL)
+           return ENOMEM;
+        groups->val = tmp;
+        groups->val[groups->len].vo = strdup(vo_name);
+        groups->val[groups->len].name = strdup((*voms_data)->group);
+        groups->len++;
+      }
+   }
+   return 0;
+}
+
+static int 
+get_groups(edg_wll_Context ctx, struct vomsdata *voms_info,
+          edg_wll_VomsGroups *res_groups)
+{
+   struct voms **voms_cert = NULL;
+   edg_wll_VomsGroups groups;
+   int ret;
+
+   memset(&groups, 0, sizeof(groups));
+
+   for (voms_cert = voms_info->data; voms_cert && *voms_cert; voms_cert++) {
+      if ((*voms_cert)->voname) {
+        ret = add_groups(ctx, *voms_cert, (*voms_cert)->voname, &groups);
+        if (ret) {
+           edg_wll_FreeVomsGroups(&groups);
+           return ret;
+        }
+      }
+   }
+
+   res_groups->len = groups.len;
+   res_groups->val = groups.val;
+   return 0;
+}
+
+int
+edg_wll_GetVomsGroups(edg_wll_Context ctx, char *voms_dir, char *ca_dir)
+{
+   STACK_OF(X509) *p_chain = NULL;
+   X509 *cert = NULL;
+   int ret;
+   int err = 0;
+   struct vomsdata *voms_info = NULL;
+
+   memset (&ctx->vomsGroups, 0, sizeof(ctx->vomsGroups));
+   edg_wll_ResetError(ctx);
+
+   p_chain = SSL_get_peer_cert_chain(ctx->connPool[ctx->connToUse].ssl);
+   cert = SSL_get_peer_certificate(ctx->connPool[ctx->connToUse].ssl);
+
+   /* exit if peer's credentials are not available */
+   if (p_chain == NULL || cert == NULL) {
+      ret = 0;
+      goto end;
+   }
+      
+   /* uses X509_CERT_DIR and X509_VOMS_DIR vars */
+   voms_info = VOMS_Init(voms_dir, ca_dir);
+   if (voms_info == NULL) {
+      edg_wll_SetError(ctx, errno, "failed to initialize VOMS structures");
+      ret = -1; /* XXX VOMS Error */
+      goto end;
+   }
+
+   ret = VOMS_Retrieve(cert, p_chain, RECURSE_CHAIN, voms_info, &err);
+   if (ret == 0) {
+      if (err == VERR_NOEXT)
+        edg_wll_SetError(ctx, EINVAL, "no client VOMS certificates found");
+      else {
+        edg_wll_SetError(ctx, -1, "failed to retrieve VOMS info");
+        ret = -1; /* XXX VOMS Error */
+      }
+      goto end;
+   }
+
+   ret = get_groups(ctx, voms_info, &ctx->vomsGroups);
+
+end:
+   if (voms_info)
+      VOMS_Destroy(voms_info);
+   if (cert)
+      X509_free(cert);
+
+   return ret;
+}
+
+void
+edg_wll_FreeVomsGroups(edg_wll_VomsGroups *groups)
+{
+   size_t len;
+
+   if (groups == NULL)
+      return;
+
+   for (len = 0; len < groups->len; len++) {
+      if (groups->val[len].vo)
+        free(groups->val[len].vo);
+      if (groups->val[len].name)
+        free(groups->val[len].name);
+   }
+}
+
+
+static int
+parse_creds(edg_wll_VomsGroups *groups, char *subject, GACLuser **gacl_user)
+{
+   GACLcred *cred = NULL;
+   GACLuser *user = NULL;
+   int ret;
+   int i;
+
+   GACLinit();
+
+   cred = GACLnewCred("person");
+   if (cred == NULL)
+      return ENOMEM;
+   
+   if (!GACLaddToCred(cred, "dn", subject)) {
+      ret = EINVAL; /* GACL_ERR */
+      goto fail;
+   }
+
+   user = GACLnewUser(cred);
+   if (user == NULL) {
+      ret = ENOMEM;
+      goto fail;
+   }
+   cred = NULL; /* GACLnewUser() doesn't copy content, just store the pointer */
+
+   for (i = 0; i < groups->len; i++) {
+      cred = GACLnewCred("voms-cred");
+      if (cred == NULL) {
+        ret = ENOMEM;
+        goto fail;
+      }
+      if (!GACLaddToCred(cred, "vo", groups->val[i].vo) ||
+         !GACLaddToCred(cred, "group", groups->val[i].name)) {
+        ret = EINVAL; /* GACL_ERR */
+        goto fail;
+      }
+      if (!GACLuserAddCred(user, cred)) {
+        ret = EINVAL; /* GACL_ERR */
+        goto fail;
+      }
+      cred = NULL;
+      /* GACLuserAddCred() doesn't copy content, just store the pointer. Cred
+       * mustn't be free()ed */
+   }
+
+   *gacl_user = user;
+
+   return 0;
+
+fail:
+   if (cred)
+      GACLfreeCred(cred);
+   if (user)
+      GACLfreeUser(user);
+
+   return ret;
+}
+
+static int
+cmp_gacl_names(struct _GACLnamevalue *n1, struct _GACLnamevalue *n2)
+{
+   if (n1 == NULL && n2 == NULL)
+      return 1;
+
+   for ( ; n1; n1 = n1->next, n2 = n2->next) {
+      if (n2 == NULL)
+        return 0;
+      if (strcmp(n1->name, n2->name) != 0 ||
+         strcmp(n1->value, n2->value) != 0)
+        return 0;
+   }
+
+   return (n2 == NULL);
+}
+
+static int
+cmp_gacl_creds(GACLcred *c1, GACLcred *c2)
+{
+   if (strcmp(c1->type, c2->type) != 0)
+      return 0;
+
+   return cmp_gacl_names(c1->firstname, c2->firstname);
+   /* we support only "simple" entries containing only one credential (DN or 
+    * VOMS group */
+}
+
+static int
+addEntry(GACLacl *acl, GACLentry *entry)
+{
+       GACLentry   *cur = NULL;
+   
+
+       if ( acl == NULL )
+               return EINVAL;
+
+       if ( acl->firstentry == NULL )
+               return (GACLaddEntry(acl, entry) == 0) ? -1 /* GACL_ERR */ : 0;
+
+       for ( cur = acl->firstentry; cur; cur = cur->next )
+               if (   cmp_gacl_creds(cur->firstcred, entry->firstcred)
+                       && cur->allowed == entry->allowed
+                       && cur->denied == entry->denied ) 
+                       return EEXIST;
+
+       return (GACLaddEntry(acl, entry) == 0) ? -1 /* GACL_ERR */ : 0;
+}
+
+static int
+delEntry(GACLacl *acl, GACLentry *entry)
+{
+   GACLentry *cur = NULL, *prev = NULL;
+   int found = 0;
+   
+   if (acl == NULL || acl->firstentry == NULL)
+      return EINVAL;
+
+   cur = acl->firstentry;
+   while (cur) {
+      if (cmp_gacl_creds(cur->firstcred, entry->firstcred) &&
+         cur->allowed == entry->allowed &&
+         cur->denied == entry->denied) {
+        if (prev)
+           prev->next = cur->next;
+        else
+           acl->firstentry = cur->next;
+        GACLfreeEntry(cur);
+        found = 1;
+        break;
+      }
+      prev = cur;
+      cur = cur->next; 
+   }
+
+   return (found) ? 0 : -1 /* NOT_FOUND */;
+}
+
+static int
+create_cred(char *userid, int user_type, GACLcred **cred)
+{
+   GACLcred *c = NULL;
+   char *group = NULL;
+
+   if (user_type == EDG_WLL_USER_SUBJECT) {
+      c = GACLnewCred("person");
+      if (c == NULL)
+        return ENOMEM;
+      if (!GACLaddToCred(c, "dn", userid)) {
+        GACLfreeCred(c);
+        return -1; /* GACL_ERR */
+      }
+   } else if(user_type == EDG_WLL_USER_VOMS_GROUP) {
+      c = GACLnewCred("voms-cred");
+      if (c == NULL)
+        return ENOMEM;
+      group = strchr(userid, ':');
+      if ( !group )
+        return EINVAL;
+      *group++ = '\0';
+      if (!GACLaddToCred(c, "vo", userid) ||
+         !GACLaddToCred(c, "group", group)) {
+        GACLfreeCred(c);
+        return -1; /* GACL_ERR */
+      }
+   } else
+      return EINVAL;
+
+   *cred = c;
+
+   return 0;
+}
+
+static int
+change_acl(GACLacl *acl, GACLentry *entry, int operation)
+      /* creds, permission, permission_type */
+{
+   if (operation == EDG_WLL_ACL_ADD)
+      return addEntry(acl, entry);
+   
+   if (operation == EDG_WLL_ACL_REMOVE)
+      return delEntry(acl, entry);
+
+   return -1;
+}
+
+static int
+edg_wll_change_acl(edg_wll_Acl acl, char *user_id, int user_id_type, 
+                  int permission, int perm_type, int operation)
+{
+   GACLcred *cred = NULL;
+   GACLentry *entry = NULL;
+   int ret;
+
+   GACLinit();
+
+   if (acl == NULL || acl->value == NULL)
+      return EINVAL;
+
+   ret = create_cred(user_id, user_id_type, &cred);
+   if (ret)
+      return ret;
+
+   entry = GACLnewEntry();
+   if (entry == NULL) {
+      ret = ENOMEM;
+      goto end;
+   }
+
+   if (!GACLaddCred(entry, cred)) {
+      ret = -1; /* GACLErr */
+      goto end;
+   }
+
+   if (perm_type == EDG_WLL_PERM_ALLOW)
+      GACLallowPerm(entry, permission);
+   else if (perm_type == EDG_WLL_PERM_DENY)
+      GACLdenyPerm(entry, permission);
+   else {
+      ret = EINVAL;
+      goto end;
+   }
+
+   ret = change_acl(acl->value, entry, operation);
+   if (ret)
+   {
+/*    XXX: mem leak?
+      GACLfreeEntry(entry);
+*/
+      goto end;
+   }
+
+   if (acl->string) free(acl->string);
+   ret = edg_wll_EncodeACL(acl->value, &acl->string);
+
+end:
+
+   return ret;
+}
+
+int
+edg_wll_CheckACL(edg_wll_Context ctx, edg_wll_Acl acl, int requested_perm)
+{
+   int ret;
+   GACLuser *user = NULL;
+   GACLperm perm;
+
+   if (acl == NULL || acl->value == NULL)
+      return edg_wll_SetError(ctx,EINVAL,"CheckACL");
+
+   if (!ctx->peerName) return edg_wll_SetError(ctx,EPERM,"CheckACL");
+
+   ret = parse_creds(&ctx->vomsGroups, ctx->peerName, &user);
+   if (ret) {
+      return edg_wll_SetError(ctx,ret,"parse_creds()");
+   }
+
+   perm = GACLtestUserAcl(acl->value, user);
+
+   GACLfreeUser(user);
+   
+   if (perm & requested_perm) return edg_wll_ResetError(ctx);
+   else return edg_wll_SetError(ctx,EPERM,"CheckACL");
+}
+
+int
+edg_wll_EncodeACL(GACLacl *acl, char **str)
+{
+   int tmp_fd, ret;
+   FILE *fd = NULL;
+   char filename[16];
+   char line[4096];
+   char *buf = NULL;
+   size_t buf_len = 0;
+   char *p;
+
+   snprintf(filename, sizeof(filename), "/tmp/XXXXXX");
+   tmp_fd = mkstemp(filename);
+   if (tmp_fd == -1)
+      return errno;
+
+   fd = fdopen(tmp_fd, "r");
+
+   ret = GACLsaveAcl(filename, acl);
+   unlink(filename);
+   if (ret == 0) {
+      ret = -1; /* GACL_ERR */
+      goto end;
+   }
+
+   buf_len = 1024;
+   buf = calloc(buf_len, 1);
+   if (buf == NULL) {
+      ret = ENOMEM;
+      goto end;
+   }
+
+   while (fgets(line, sizeof(line), fd) != NULL) {
+      p = strchr(line, '\n');
+      if (p)
+        *p = '\0';
+
+      if (strlen(buf) + strlen(line) > buf_len) {
+        char *tmp;
+
+        tmp =  realloc(buf, buf_len + 1024);
+        if (tmp == NULL) {
+           ret = ENOMEM;
+           goto end;
+        }
+        buf = tmp;
+        buf_len += 1024;
+      }
+
+      strcat(buf, line);
+   }
+
+   *str = buf;
+   ret = 0;
+
+end:
+   fclose(fd);
+   return ret;
+}
+
+int
+edg_wll_DecodeACL(char *buf, GACLacl **result_acl)
+{
+   /* Got from GACLloadAcl() available from GACL API */
+   xmlDocPtr   doc;
+   xmlNodePtr  cur;
+   GACLacl    *acl;
+   GACLentry  *entry;
+        
+   doc = xmlParseMemory(buf, strlen(buf));
+   if (doc == NULL) return EINVAL;
+    
+   cur = xmlDocGetRootElement(doc);
+  
+   if (xmlStrcmp(cur->name, (const xmlChar *) "gacl"))
+    {
+       free(doc);
+       free(cur);
+       return EINVAL;
+    }
+
+   cur = cur->xmlChildrenNode;
+
+   acl = GACLnewAcl();
+  
+   while (cur != NULL)
+       {
+        /*
+        if (cur->type == XML_TEXT_NODE && cur->content == '\n') {
+           cur=cur->next;
+           continue;
+        }
+        */
+         entry = GACLparseEntry(cur);
+         if (entry == NULL)
+           {
+             GACLfreeAcl(acl);
+             xmlFreeDoc(doc);
+             return EINVAL;
+           }
+
+         GACLaddEntry(acl, entry);
+         
+         cur=cur->next;
+       }
+
+   xmlFreeDoc(doc);
+   *result_acl = acl;
+   return 0;
+}
+
+int
+edg_wll_InitAcl(edg_wll_Acl *acl)
+{
+   edg_wll_Acl tmp;
+
+   tmp = malloc(sizeof(*tmp));
+   if ( !tmp )
+      return ENOMEM;
+
+   tmp->value = GACLnewAcl();
+   tmp->string = NULL;
+   *acl = tmp;
+   return 0;
+}
+
+void
+edg_wll_FreeAcl(edg_wll_Acl acl)
+{
+   if ( acl->value ) GACLfreeAcl(acl->value);
+   if ( acl->string ) free(acl->string);
+   free(acl);
+}
+
+int
+edg_wll_HandleCounterACL(edg_wll_Context ctx, edg_wll_Acl acl,
+                        char *aclid, int incr)
+{
+       char       *q1 = NULL,
+                          *q2 = NULL;
+
+       edg_wll_ResetError(ctx);
+
+       if ( incr > 0 )
+       {
+               trio_asprintf(&q1,
+                               "insert into acls(aclid,value,refcnt) "
+                               "values ('%|Ss','%|Ss',%d)",
+                               aclid, acl->string, incr);
+
+               for ( ; ; )
+               {
+                       if ( edg_wll_ExecStmt(ctx, q1, NULL) > 0 )
+                               goto end;
+
+                       if ( edg_wll_Error(ctx,NULL,NULL) != EEXIST )
+                               goto end;
+
+                       /*
+                        *      row allready in DB
+                        */
+                       if ( !q2 ) trio_asprintf(&q2,
+                                               "update acls set refcnt = refcnt+%d "
+                                               "where aclid = '%|Ss'",
+                                               incr, aclid);
+                       if ( edg_wll_ExecStmt(ctx, q2, NULL) < 0 )
+                               continue;
+
+                       goto end; 
+               }
+       }
+       else if (incr < 0)
+       {
+               trio_asprintf(&q1,
+                               "update acls set refcnt = refcnt-%d "
+                               "where aclid='%|Ss' and refcnt>=%d",
+                               -incr, aclid, -incr);
+
+               if ( edg_wll_ExecStmt(ctx, q1, NULL) > 0 )
+               {
+                       trio_asprintf(&q2,
+                                               "delete from acls "
+                                               "where aclid='%|Ss' and refcnt=0",
+                                               aclid);
+                       edg_wll_ExecStmt(ctx, q2, NULL);
+               }
+               else
+               {
+                       fprintf(stderr, "ACL with ID: %s has invalid reference count\n", aclid);
+                       syslog(LOG_WARNING, "ACL with ID: %s has invalid reference count\n", aclid);
+               }
+       }
+
+
+end:
+       if ( q1 ) free(q1);
+       if ( q2 ) free(q2);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+int
+edg_wll_UpdateACL(edg_wll_Context ctx, edg_wlc_JobId job, 
+                 char *user_id, int user_id_type,
+                 int permission, int perm_type, int operation)
+{
+   char *md5_jobid;
+   edg_wll_Acl acl = NULL;
+   int ret;
+   char *stmt = NULL;
+   char *new_aclid = NULL, *old_aclid = NULL;
+   int updated;
+
+   edg_wll_ResetError(ctx);
+
+   md5_jobid = edg_wlc_JobIdGetUnique(job);
+
+   do {
+      if (acl)
+      {
+        edg_wll_FreeAcl(acl);
+        acl = NULL;
+      }
+      if (old_aclid)
+      {
+        free(old_aclid);
+        old_aclid = NULL;
+      }
+      if (new_aclid)
+      {
+        free(new_aclid);
+         new_aclid = NULL;
+      }
+        
+      if ( (ret = edg_wll_GetACL(ctx, job, &acl)) )
+        goto end;
+      if ( !acl && (ret = edg_wll_InitAcl(&acl)) )
+        goto end;
+        
+      old_aclid = acl->string? strdup(strmd5(acl->string, NULL)): NULL;
+
+      ret = edg_wll_change_acl(acl, user_id, user_id_type, 
+                              permission, perm_type, operation);
+      if (ret)
+      {
+        if ( ret == EEXIST )
+           /*
+            *  adding allready set entry
+            *  only upgrade the counter
+            */
+           ret = edg_wll_HandleCounterACL(ctx, acl, new_aclid, 1);
+
+        goto end;
+      }
+
+      new_aclid = strdup(strmd5(acl->string, NULL));
+
+      /* store new ACL or increment its counter if already present in db */
+      ret = edg_wll_HandleCounterACL(ctx, acl, new_aclid, 1);
+      if  (ret)
+        goto end;
+
+      if ( old_aclid )
+        trio_asprintf(&stmt,
+           "update jobs set aclid='%|Ss' where jobid='%|Ss' and aclid='%|Ss'",
+           new_aclid, md5_jobid, old_aclid);
+      else
+        trio_asprintf(&stmt,
+           "update jobs set aclid='%|Ss' where jobid='%|Ss' and ISNULL(aclid)",
+           new_aclid, md5_jobid);
+      updated = edg_wll_ExecStmt(ctx, stmt, NULL);
+      free(stmt); stmt = NULL;
+
+      if (updated > 0)
+        /* decrement reference counter of the old ACL, and possibly remove
+         * whole ACL if the counter becames zero */
+        ret = edg_wll_HandleCounterACL(ctx, NULL, old_aclid, -1);
+      else
+         /* We failed to store new ACL to db, most likely because the ACL has
+         * been changed. Decrement counter of new ACL set before trying
+         * updating */
+        ret = edg_wll_HandleCounterACL(ctx, NULL, new_aclid, -1);
+   } while (updated <= 0);
+
+end:
+   free(md5_jobid);
+   if (acl)
+      edg_wll_FreeAcl(acl);
+   if (new_aclid)
+      free(new_aclid);
+   if (old_aclid)
+      free(old_aclid);
+
+   return ret;
+}
+
+int edg_wll_GetACL(edg_wll_Context ctx, edg_wlc_JobId jobid, edg_wll_Acl *acl)
+{
+       char    *q = NULL;
+       char    *acl_id = NULL;
+       char    *acl_str = NULL;
+       edg_wll_Stmt    stmt = NULL;
+       int     ret;
+       GACLacl *gacl = NULL;
+       char    *jobstr = edg_wlc_JobIdGetUnique(jobid);
+
+       if (jobid == NULL || jobstr == NULL)
+          return edg_wll_SetError(ctx,EINVAL,"edg_wll_GetACL()");
+
+       edg_wll_ResetError(ctx);
+
+       trio_asprintf(&q,
+               "select aclid from jobs where jobid = '%|Ss'", jobstr);
+
+       if (edg_wll_ExecStmt(ctx, q, &stmt) < 0 ||
+               edg_wll_FetchRow(stmt, &acl_id) < 0) {
+               goto end;
+       }
+       edg_wll_FreeStmt(&stmt); stmt = NULL;
+       free(q); q = NULL;
+
+       if (acl_id == NULL || *acl_id == '\0') {
+               *acl = NULL;
+               return 0;
+       }
+
+       trio_asprintf(&q,
+               "select value from acls where aclid = '%|Ss'", acl_id);
+       if (edg_wll_ExecStmt(ctx, q, &stmt) < 0 ||
+               edg_wll_FetchRow(stmt, &acl_str) < 0) {
+               goto end;
+       }
+
+       ret = edg_wll_DecodeACL(acl_str, &gacl);
+       if (ret) {
+               edg_wll_SetError(ctx, EINVAL, "encoding ACL");
+               goto end;
+       }
+
+       *acl = calloc(1, sizeof(**acl));
+       if (*acl == NULL) {
+               ret = ENOMEM;
+               edg_wll_SetError(ctx, ENOMEM, "not enough memory");
+               goto end;
+       }
+
+       (*acl)->value = gacl;
+       (*acl)->string = acl_str;
+       gacl = NULL; acl_str = NULL;
+       ret = 0;
+
+end:
+       if (q) free(q);
+       if (stmt) edg_wll_FreeStmt(&stmt);
+       if (acl_id) free(acl_id);
+       if (acl_str) free(acl_str);
+       if (gacl) GACLfreeAcl(gacl);
+       if (jobstr) free(jobstr);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
diff --git a/org.glite.lb.server/src/lb_authz.h b/org.glite.lb.server/src/lb_authz.h
new file mode 100644 (file)
index 0000000..43ffc1a
--- /dev/null
@@ -0,0 +1,42 @@
+#ifndef LB_AUTHZ_H
+#define LB_AUTHZ_H
+
+#include <stdio.h>
+#include <gacl.h>
+
+#include "glite/lb/context-int.h"
+
+typedef struct _edg_wll_Acl {
+       GACLacl *value;
+       char    *string;
+} _edg_wll_Acl;
+typedef struct _edg_wll_Acl *edg_wll_Acl;
+
+extern int
+edg_wll_InitAcl(edg_wll_Acl *);
+
+extern void
+edg_wll_FreeAcl(edg_wll_Acl);
+
+extern int
+edg_wll_GetVomsGroups(edg_wll_Context, char *, char *);
+
+extern void
+edg_wll_FreeVomsGroups(edg_wll_VomsGroups *);
+
+extern int
+edg_wll_UpdateACL(edg_wll_Context, edg_wlc_JobId, char *, int, int, int, int);
+
+extern int
+edg_wll_CheckACL(edg_wll_Context, edg_wll_Acl, int);
+
+extern int
+edg_wll_DecodeACL(char *, GACLacl **);
+
+extern int
+edg_wll_EncodeACL(GACLacl *, char **);
+
+extern int
+edg_wll_GetACL(edg_wll_Context, edg_wlc_JobId, edg_wll_Acl *);
+
+#endif
diff --git a/org.glite.lb.server/src/lb_html.c b/org.glite.lb.server/src/lb_html.c
new file mode 100644 (file)
index 0000000..e91a3ba
--- /dev/null
@@ -0,0 +1,146 @@
+#ident "$Header$"
+
+#include "lb_html.h"
+#include "lb_proto.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+int edg_wll_QueryToHTML(edg_wll_Context ctx UNUSED_VAR, edg_wll_Event *eventsOut UNUSED_VAR, char **message UNUSED_VAR)
+{
+/* not implemented yet */
+       return -1;
+}
+
+/* construct Message-Body of Response-Line for edg_wll_UserJobs */
+int edg_wll_UserJobsToHTML(edg_wll_Context ctx, edg_wlc_JobId *jobsOut, char **message)
+{
+        char *pomA, *pomB;
+        int i = 0;
+
+       /* head */
+       pomB = strdup("");      
+
+        while (jobsOut[i]) {
+               char    *chid = edg_wlc_JobIdUnparse(jobsOut[i]);
+
+                asprintf(&pomA,"%s\t\t <li> <a href=\"%s\">%s</a>\r\n",
+                        pomB, chid,chid);
+
+               free(chid);
+                free(pomB);
+                pomB = pomA;
+                i++;
+        }
+
+        asprintf(&pomA, "<html>\r\n\t<body>\r\n"
+                       "<h2><B>User jobs</B></h2>\r\n"
+                       "User subject: %s<p>"
+                       "<ul>%s</ul>"
+                       "\t</body>\r\n</html>",ctx->peerName,pomB);
+        free(pomB);
+
+        *message = pomA;
+
+        return 0;
+}
+
+
+/* construct Message-Body of Response-Line for edg_wll_JobStatus */
+int edg_wll_JobStatusToHTML(edg_wll_Context ctx UNUSED_VAR, edg_wll_JobStat stat, char **message)
+{
+        char *pomA, *pomB;
+       char    *chid,*chstat;
+       char    *jdl,*rsl;
+
+       jdl = strdup("");
+       rsl = strdup("");
+       
+       pomB = strdup("");
+
+        chid = edg_wlc_JobIdUnparse(stat.jobId);
+
+#define TR(name,type,field)            \
+       if (field) {            \
+               asprintf(&pomA,"%s<tr><th align=\"left\">" name ":</th>"        \
+                       "<td>" type "</td></tr>",pomB,(field)); \
+               free(pomB);                                     \
+               pomB = pomA;                                    \
+       }
+
+       TR("Status","%s",(chstat = edg_wll_StatToString(stat.state)));
+       free(chstat);
+       TR("owner","%s",stat.owner);
+       TR("Condor Id","%s",stat.condorId);
+       TR("Globus Id","%s",stat.globusId);
+       TR("Local Id","%s",stat.localId);
+       TR("Reason","%s",stat.reason);
+       if ( (stat.stateEnterTime.tv_sec) || (stat.stateEnterTime.tv_usec) ) {
+               time_t  time = stat.stateEnterTime.tv_sec;
+               TR("State entered","%s",ctime(&time));
+       }
+        if ( (stat.lastUpdateTime.tv_sec) || (stat.lastUpdateTime.tv_usec) ) {
+               time_t  time = stat.lastUpdateTime.tv_sec;
+               TR("Last update","%s",ctime(&time));
+       }
+       TR("Expect update","%s",stat.expectUpdate ? "YES" : "NO");
+       TR("Expect update from","%s",stat.expectFrom);
+       TR("Location","%s",stat.location);
+       TR("Destination","%s",stat.destination);
+       TR("Cancelling","%s",stat.cancelling>0 ? "YES" : "NO");
+       if (stat.cancelReason != NULL) {
+               TR("Cancel reason","%s",stat.cancelReason);
+       }
+       TR("CPU time","%d",stat.cpuTime);
+
+       
+       TR("Done code","%d",stat.done_code);
+       TR("Exit code","%d",stat.exit_code);
+
+        if (stat.jdl) asprintf(&jdl,"<h3>Job description</h3>\r\n"
+               "<pre>%s</pre>\r\n",stat.jdl);
+
+       if (stat.rsl) asprintf(&rsl,"<h3>RSL</h3>\r\n"
+               "<pre>%s</pre>\r\n",stat.rsl);
+
+
+        asprintf(&pomA, "<html>\r\n\t<body>\r\n"
+                       "<h2>%s</h2>\r\n"
+                       "<table halign=\"left\">%s</table>"
+                       "%s%s"
+                       "\t</body>\r\n</html>",
+                       chid,pomB,jdl,rsl);
+        free(pomB);
+
+        *message = pomA;
+
+       free(chid);
+       free(jdl);
+       free(rsl);
+        return 0;
+}
+
+char *edg_wll_ErrorToHTML(edg_wll_Context ctx,int code)
+{
+       char    *out,*et,*ed;
+       char    *msg = edg_wll_HTTPErrorMessage(code);
+       edg_wll_ErrorCode       e;
+
+       e = edg_wll_Error(ctx,&et,&ed);
+       asprintf(&out,"<html><head><title>Error</title></head>\n"
+               "<body><h1>%s</h1>\n"
+               "%d: %s (%s)</body></html>",msg,e,et,ed);
+
+       free(et); free(ed);
+       return out;
+}
diff --git a/org.glite.lb.server/src/lb_html.h b/org.glite.lb.server/src/lb_html.h
new file mode 100644 (file)
index 0000000..1cac83f
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _LB_HTML
+#define _LB_HTML
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+
+int edg_wll_QueryToHTML(edg_wll_Context,edg_wll_Event *,char **);
+int edg_wll_JobStatusToHTML(edg_wll_Context, edg_wll_JobStat, char **);
+int edg_wll_UserJobsToHTML(edg_wll_Context, edg_wlc_JobId *, char **);
+char *edg_wll_ErrorToHTML(edg_wll_Context,int);
+
+#endif
diff --git a/org.glite.lb.server/src/lb_http.c b/org.glite.lb.server/src/lb_http.c
new file mode 100644 (file)
index 0000000..79e85d5
--- /dev/null
@@ -0,0 +1,53 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/mini_http.h"
+#include "glite/lb/context-int.h"
+
+#include "lb_http.h"
+#include "lb_proto.h"
+
+#define dprintf(x) printf x
+
+
+int edg_wll_ServerHTTP(edg_wll_Context ctx)
+{
+       char    **hdr = NULL,*req = NULL,*body = NULL,
+               **hdrOut = NULL, *resp = NULL, *bodyOut = NULL,
+               *err_desc = NULL;
+       edg_wll_ErrorCode       err = 0;
+
+
+       err = edg_wll_http_recv(ctx,&req,&hdr,&body);
+
+       dprintf(("[%d] %s\n",getpid(),req));
+       if (body) dprintf(("\n%s\n\n",body));
+
+       if (!err) {
+               if ((err = edg_wll_Proto(ctx,req,hdr,body,&resp,&hdrOut,&bodyOut))) 
+                       edg_wll_Error(ctx,NULL,&err_desc);
+
+               if (resp) edg_wll_http_send(ctx,resp,(char const * const *)hdrOut,bodyOut);
+       }
+
+       free(req);
+       free(resp);
+       if (hdr) {
+               char    **h;
+               for (h = hdr; *h; h++) free(*h);
+               free(hdr);
+       }
+       // hdrOut are static
+       free(body);
+       free(bodyOut);
+
+       if (err != edg_wll_Error(ctx,NULL,NULL)) edg_wll_SetError(ctx,err,err_desc);
+       free(err_desc);
+       return err;
+}
diff --git a/org.glite.lb.server/src/lb_http.h b/org.glite.lb.server/src/lb_http.h
new file mode 100644 (file)
index 0000000..c3e012d
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _LB_HTTP_H
+#define _LB_HTTP_H
+
+#ident "$Header$"
+
+#include <stdio.h>
+
+#include "glite/lb/consumer.h"
+
+int edg_wll_ServerHTTP(edg_wll_Context);
+
+#endif
diff --git a/org.glite.lb.server/src/lb_proto.c b/org.glite.lb.server/src/lb_proto.c
new file mode 100644 (file)
index 0000000..240f795
--- /dev/null
@@ -0,0 +1,565 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <expat.h>
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/mini_http.h"
+#include "glite/lb/xml_conversions.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/load.h"
+#include "glite/lb/purge.h"
+
+#include "lb_proto.h"
+#include "lb_html.h"
+#include "get_events.h"
+#include "purge.h"
+#include "lb_xml_parse.h"
+
+
+#define METHOD_GET      "GET "
+#define METHOD_POST     "POST "
+
+#define KEY_JOBS               "/userJobs/"
+#define KEY_QUERY_JOBS         "/queryJobs "
+#define KEY_QUERY_EVENTS       "/queryEvents "
+#define KEY_PURGE_REQUEST      "/purgeRequest "
+#define KEY_DUMP_REQUEST       "/dumpRequest "
+#define KEY_LOAD_REQUEST       "/loadRequest "
+#define KEY_INDEXED_ATTRS      "/indexedAttrs "
+#define KEY_NOTIF_REQUEST      "/notifRequest "
+#define KEY_HTTP               "HTTP/1.1"
+
+
+#define KEY_ACCEPT      "Accept:"
+#define KEY_APP         "application/x-dglb"
+#define KEY_AGENT      "User-Agent"
+
+
+const char* const response_headers[] = {
+        "Cache-Control: no-cache",
+        "Accept: application/x-dglb",
+        "User-Agent: edg_wll_Server/" PROTO_VERSION "/" COMP_PROTO,
+        "Content-Type: application/x-dglb",
+        NULL
+};
+
+extern int edg_wll_NotifNewServer(edg_wll_Context,
+                               edg_wll_QueryRec const * const *, char const *,
+                               const edg_wll_NotifId, time_t *);
+extern int edg_wll_NotifBindServer(edg_wll_Context,
+                               const edg_wll_NotifId, const char *, time_t *);
+extern int edg_wll_NotifChangeServer(edg_wll_Context,
+                               const edg_wll_NotifId, edg_wll_QueryRec const * const *,
+                               edg_wll_NotifChangeOp);
+extern int edg_wll_NotifRefreshServer(edg_wll_Context,
+                               const edg_wll_NotifId, time_t *);
+extern int edg_wll_NotifDropServer(edg_wll_Context, edg_wll_NotifId *);
+
+
+
+char *edg_wll_HTTPErrorMessage(int errCode)
+{
+       char *msg;
+       
+       switch (errCode) {
+               case HTTP_OK: msg = "OK"; break;
+               case HTTP_BADREQ: msg = "Bad Request"; break;
+               case HTTP_UNAUTH: msg = "Unauthorized"; break;
+               case HTTP_NOTFOUND: msg = "Not Found"; break;
+               case HTTP_NOTALLOWED: msg = "Method Not Allowed"; break;
+               case HTTP_UNSUPPORTED: msg = "Unsupported Media Type"; break;
+               case HTTP_NOTIMPL: msg = "Not Implemented"; break;
+               case HTTP_INTERNAL: msg = "Internal Server Error"; break;
+               case HTTP_UNAVAIL: msg = "Service Unavailable"; break;
+               case HTTP_INVALID: msg = "Invalid Data"; break;
+               default: msg = "Unknown error"; break;
+       }
+
+       return msg;
+}
+
+/* returns non-zero if protocols incompatible */
+static int is_protocol_incompatible(char *user_agent)
+{
+        char *version, *comp_proto, *needle;
+        double  v, c, my_v = strtod(PROTO_VERSION, (char **) NULL), my_c;
+
+
+       /* get version od the other side */
+        if ((version = strstr(user_agent,"/")) == NULL) return(-1);
+        else v = strtod(++version, &needle);
+
+       /* sent the other side list of compatible protocols? */
+       if ( needle[0] == '\0' ) return(-2);
+
+       /* test compatibility if server newer*/
+        if (my_v > v) {
+                comp_proto=COMP_PROTO;
+                do {
+                        my_c = strtod(comp_proto, &needle);
+                        if (my_c == v) return(0);
+                        comp_proto = needle + 1;
+                } while (needle[0] != '\0');
+                return(1);
+        }
+
+       /* test compatibility if server is older */
+        else if (my_v < v) {
+                do {
+                        comp_proto = needle + 1;
+                        c = strtod(comp_proto, &needle);
+                        if (my_v == c) return(0);
+                } while (needle[0] != '\0');
+                return(1);
+        }
+
+       /* version match */
+        return(0);
+}
+
+
+static int outputHTML(char **headers)
+{
+       int i;
+
+       if (!headers) return 0;
+
+       for (i=0; headers[i]; i++)
+               if (!strncmp(headers[i], KEY_ACCEPT, sizeof(KEY_ACCEPT) - 1)) {
+                       if (strstr(headers[i],KEY_APP)) 
+                               return 0;               /* message sent by edg_wll_Api */
+                       else 
+                               return 1;               /* message sent by other application */
+               }
+       return 1;
+
+}
+
+
+
+edg_wll_ErrorCode edg_wll_Proto(edg_wll_Context ctx,
+       char *request,char **headers,char *messageBody,
+       char **response,char ***headersOut,char **bodyOut)
+{
+       //char *requestPTR, *pom, *message = NULL; Ely 16/10 unused variable `pom'
+       char *requestPTR, *message = NULL;
+       int     ret = HTTP_OK;
+       int     html = outputHTML(headers);
+       int     i;
+
+       edg_wll_ResetError(ctx);
+
+       for (i=0; headers[i]; i++) /* find line with version number in headers */
+               if ( strstr(headers[i], KEY_AGENT) ) break;
+  
+       if (headers[i] == NULL) { ret = HTTP_BADREQ; goto err; } /* if not present */
+       switch (is_protocol_incompatible(headers[i])) { 
+               case 0  : /* protocols compatible */
+                         break;
+               case -1 : /* malformed 'User Agent:' line */
+                         ret = HTTP_BADREQ;
+                         goto err;
+                         break;
+               case -2 : /* version of one protocol unknown */
+                         /* fallthrough */
+               case 1  : /* protocols incompatible */
+                         /* fallthrough */
+               default : ret = HTTP_UNSUPPORTED; 
+                         edg_wll_SetError(ctx,ENOTSUP,"Protocol versions are incompatible.");
+                         goto err; 
+                         break;
+       }
+
+
+/* GET */
+       if (!strncmp(request, METHOD_GET, sizeof(METHOD_GET)-1)) {
+               
+               requestPTR = request + sizeof(METHOD_GET)-1;
+
+
+       /* GET /: Current User Jobs */
+               if (requestPTR[0]=='/' && (requestPTR[1]==' ' || requestPTR[1]=='?')) {
+                       edg_wlc_JobId *jobsOut = NULL;
+                       int     i, flags;
+                       
+                       flags = (requestPTR[1]=='?') ? edg_wll_string_to_stat_flags(requestPTR + 2) : 0;
+
+// FIXME: edg_wll_UserJobs should take flags as parameter
+                       if (!ctx->peerName) {
+                               edg_wll_SetError(ctx,EPERM,"Operation not permitted.");
+                               ret = HTTP_UNAUTH; 
+                       }
+                       switch (edg_wll_UserJobs(ctx,&jobsOut,NULL)) {
+                               case 0: if (html) edg_wll_UserJobsToHTML(ctx, jobsOut, &message);
+                                       else ret = HTTP_OK;
+                                       break;
+                               case ENOENT: ret = HTTP_NOTFOUND; break;
+                               default: ret = HTTP_INTERNAL; break;
+                       }
+                       if (!html && (ret != HTTP_INTERNAL)) 
+                               if (edg_wll_UserJobsToXML(ctx, jobsOut, &message))
+                                       ret = HTTP_INTERNAL;
+
+                       if (jobsOut) {
+                               for (i=0; jobsOut[i]; i++) edg_wlc_JobIdFree(jobsOut[i]);
+                               free(jobsOut);
+                       }
+               } 
+
+       /* GET /[jobId]: Job Status */
+               else if (*requestPTR=='/') {
+                       edg_wlc_JobId jobId = NULL;
+                       char *pom1,*fullid = NULL;
+                       edg_wll_JobStat stat;
+                       char *pomCopy;
+
+                       if (ctx->srvName == NULL) {
+                               edg_wll_SetError(ctx, EDG_WLL_ERROR_SERVER_RESPONSE,
+                                                       "no server name on GET /jobid");
+                               ret = HTTP_INTERNAL;
+                               goto err;
+                       }
+                       memset(&stat,0,sizeof(stat));
+                       pomCopy = strdup(requestPTR + 1);
+                       for (pom1=pomCopy; *pom1 && !isspace(*pom1); pom1++);
+                       *pom1 = 0;
+
+                       asprintf(&fullid,GLITE_WMSC_JOBID_PROTO_PREFIX"%s:%u/%s",ctx->srvName,ctx->srvPort,pomCopy);
+                       free(pomCopy);  
+
+                       if (edg_wlc_JobIdParse(fullid, &jobId)) {
+                               edg_wll_SetError(ctx,EDG_WLL_ERROR_JOBID_FORMAT,fullid);
+                               ret = HTTP_BADREQ;
+                       }
+                       else switch (edg_wll_JobStatus(ctx,jobId,0,&stat)) {
+                               case 0: if (html) edg_wll_JobStatusToHTML(ctx,stat,&message); 
+                                       else ret = HTTP_OK;
+                                       break;
+                               case ENOENT: ret = HTTP_NOTFOUND; break;
+                               case EINVAL: ret = HTTP_INVALID; break;
+                               case EPERM : ret = HTTP_UNAUTH; break;
+                               default: ret = HTTP_INTERNAL; break;
+                       }
+                       if (!html && (ret != HTTP_INTERNAL))
+                               if (edg_wll_JobStatusToXML(ctx,stat,&message))
+                                       ret = HTTP_INTERNAL;
+
+                       free(fullid);
+                       edg_wlc_JobIdFree(jobId);
+                       edg_wll_FreeStatus(&stat);
+
+       /* GET [something else]: not understood */
+               } else ret = HTTP_BADREQ;
+
+/* POST */
+       } else if (!strncmp(request,METHOD_POST,sizeof(METHOD_POST)-1)) {
+
+               requestPTR = request + sizeof(METHOD_POST)-1;
+       
+               if (!strncmp(requestPTR,KEY_QUERY_EVENTS,sizeof(KEY_QUERY_EVENTS)-1)) { 
+                       edg_wll_Event *eventsOut = NULL;
+                       edg_wll_QueryRec **job_conditions = NULL, **event_conditions = NULL;
+                       int i,j;
+
+                       if (parseQueryEventsRequest(ctx, messageBody, &job_conditions, &event_conditions)) 
+                               ret = HTTP_BADREQ;
+                               
+                       else {
+                               int     fatal = 0;
+
+                               switch (edg_wll_QueryEventsServer(ctx,ctx->noAuth,
+                                   (const edg_wll_QueryRec **)job_conditions, 
+                                   (const edg_wll_QueryRec **)event_conditions, &eventsOut)) {
+
+                                       case 0: if (html) ret = HTTP_NOTIMPL;
+                                               else      ret = HTTP_OK; 
+                                               break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM : ret = HTTP_UNAUTH; break;
+                                       case E2BIG: ret = HTTP_UNAUTH; break;
+                                       case EINVAL: ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               /* glue errors (if eny) to XML responce */ 
+                               if (!html && !fatal)
+                                       if (edg_wll_QueryEventsToXML(ctx, eventsOut, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+
+                       if (job_conditions) {
+                               for (j = 0; job_conditions[j]; j++) {
+                                       for (i = 0 ; (job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&job_conditions[j][i]);
+                                       free(job_conditions[j]);
+                               }
+                               free(job_conditions);
+                       }
+
+                       if (event_conditions) {
+                               for (j = 0; event_conditions[j]; j++) {
+                                       for (i = 0 ; (event_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&event_conditions[j][i]);
+                                       free(event_conditions[j]);
+                               }
+                               free(event_conditions);
+                       }
+       
+                       if (eventsOut != NULL) {
+                               for (i=0; eventsOut[i].type != EDG_WLL_EVENT_UNDEF; i++) 
+                                       edg_wll_FreeEvent(&(eventsOut[i]));
+                               edg_wll_FreeEvent(&(eventsOut[i])); /* free last line */
+                               free(eventsOut);
+                       }
+               }
+               else if (!strncmp(requestPTR,KEY_QUERY_JOBS,sizeof(KEY_QUERY_JOBS)-1)) { 
+                       edg_wlc_JobId *jobsOut = NULL;
+                       edg_wll_JobStat *statesOut = NULL;
+                       edg_wll_QueryRec **conditions = NULL;
+                       int i,j, flags = 0;
+
+                       if (parseQueryJobsRequest(ctx, messageBody, &conditions, &flags))
+                               ret = HTTP_BADREQ;
+
+                       else { 
+                               int             fatal = 0,
+                                               retCode;
+                               if (flags & EDG_WLL_STAT_NO_JOBS) { 
+                                       flags -= EDG_WLL_STAT_NO_JOBS;
+                                       jobsOut = NULL;
+                                       if (flags & EDG_WLL_STAT_NO_STATES) {
+                                               flags -= EDG_WLL_STAT_NO_STATES;
+                                               statesOut = NULL;
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, NULL, NULL);
+                                       }
+                                       else
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, NULL, &statesOut);
+                               }
+                               else {
+                                       if (flags & EDG_WLL_STAT_NO_STATES) {
+                                               flags -= EDG_WLL_STAT_NO_STATES;
+                                               statesOut = NULL;
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, &jobsOut, NULL);
+                                       }
+                                       else
+                                               retCode = edg_wll_QueryJobsServer(ctx, (const edg_wll_QueryRec **)conditions, flags, &jobsOut, &statesOut);
+                               }
+                               
+                               switch ( retCode ) {
+                                       // case EPERM : ret = HTTP_UNAUTH;
+                                       //              /* soft-error fall through */
+                                       case 0: if (html) ret =  HTTP_NOTIMPL;
+                                               else ret = HTTP_OK;
+
+                                               break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM: ret = HTTP_UNAUTH; break;
+                                       case E2BIG: ret = HTTP_UNAUTH; break;
+                                       case EINVAL: ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               if (!html && !fatal)
+                                       if (edg_wll_QueryJobsToXML(ctx, jobsOut, statesOut, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+
+                       if (conditions) {
+                               for (j = 0; conditions[j]; j++) {
+                                       for (i = 0; (conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&conditions[j][i]);
+                                       free(conditions[j]);
+                               }
+                               free(conditions);
+                       }
+
+                       if (jobsOut) {
+                               for (i=0; jobsOut[i]; i++) edg_wlc_JobIdFree(jobsOut[i]);
+                               free(jobsOut);
+                       }
+                       if (statesOut) {
+                               for (i=0; statesOut[i].state != EDG_WLL_JOB_UNDEF; i++) 
+                                       edg_wll_FreeStatus(&(statesOut[i]));
+                               edg_wll_FreeStatus(&(statesOut[i])); /* free last line */
+                               free(statesOut);
+                       }
+               }
+               else if (!strncmp(requestPTR,KEY_PURGE_REQUEST,sizeof(KEY_PURGE_REQUEST)-1)) {
+                       edg_wll_PurgeRequest    request;
+
+                       if ( !parsePurgeRequest(ctx,messageBody,(int (*)()) edg_wll_StringToStat,&request) )
+                               edg_wll_PurgeServer(ctx, (const edg_wll_PurgeRequest *)&request);
+
+                       if ( request.jobs )
+                       {
+                               int i;
+                               for ( i = 0; request.jobs[i]; i++ )
+                                       free(request.jobs[i]);
+                               free(request.jobs);
+                       }
+
+                       /*
+                        * response allready sent from edg_wll_PurgeServer() - return NULL results
+                        */
+                       *response = NULL;
+                       *headersOut = NULL;
+                       *bodyOut = NULL;
+                       return edg_wll_Error(ctx,NULL,NULL);
+               }
+               else if (!strncmp(requestPTR,KEY_DUMP_REQUEST,sizeof(KEY_DUMP_REQUEST)-1)) {
+                       edg_wll_DumpRequest     request;
+                       edg_wll_DumpResult      result;
+               
+                       memset(&request,0,sizeof(request));     
+                       memset(&result,0,sizeof(result));
+                       
+                       if (parseDumpRequest(ctx, messageBody, &request))
+                               ret = HTTP_BADREQ;
+                       else {
+                               int     fatal = 0;
+                               
+                               switch (edg_wll_DumpEvents(ctx,(const edg_wll_DumpRequest *) &request, &result)) {
+                                       case 0: if (html) ret = HTTP_NOTIMPL;
+                                               else      ret = HTTP_OK; 
+                                               break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM : ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               /* glue errors (if eny) to XML responce */ 
+                               if (!html && !fatal)
+                                       if (edg_wll_DumpResultToXML(ctx, &result, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+
+                       free(result.server_file);
+               }
+               else if (!strncmp(requestPTR,KEY_LOAD_REQUEST,sizeof(KEY_LOAD_REQUEST)-1)) {
+                       edg_wll_LoadRequest     request;
+                       edg_wll_LoadResult      result;
+               
+                       memset(&request,0,sizeof(request));     
+                       memset(&result,0,sizeof(result));
+                       
+                       if (parseLoadRequest(ctx, messageBody, &request))
+                               ret = HTTP_BADREQ;
+                       else {
+                               int     fatal = 0;
+                               
+                               switch (edg_wll_LoadEvents(ctx,(const edg_wll_LoadRequest *) &request, &result)) {
+                                       case 0: if (html) ret = HTTP_NOTIMPL;
+                                               else      ret = HTTP_OK; 
+                                               break;
+                                       case EEXIST: ret = HTTP_OK; break;
+                                       case EINVAL: ret = HTTP_INVALID; break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM : ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               /* glue errors (if eny) to XML responce */ 
+                               if (!html && !fatal)
+                                       if (edg_wll_LoadResultToXML(ctx, &result, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+
+                       free(result.server_file);
+               }
+               else if (!strncmp(requestPTR,KEY_INDEXED_ATTRS,sizeof(KEY_INDEXED_ATTRS)-1)) {
+                       if (!html)
+                               if (edg_wll_IndexedAttrsToXML(ctx, &message))
+                                       ret = HTTP_INTERNAL;
+               }
+               else if (!strncmp(requestPTR,KEY_NOTIF_REQUEST,sizeof(KEY_NOTIF_REQUEST)-1)) {
+                       char *function, *address;
+                       edg_wll_NotifId notifId;
+                       edg_wll_NotifChangeOp op;
+                       edg_wll_QueryRec **conditions;
+                       time_t validity = -1;
+                       int i,j;
+                       
+                       
+                       if (parseNotifRequest(ctx, messageBody, &function, &notifId, 
+                                               &address, &op, &conditions))
+                               ret = HTTP_BADREQ;
+                       else {
+                               int     fatal = 0, err = 0;
+                               
+                               // XXX presne poradi parametru zatim neni znamo
+                               // navratove chyby nejsou zname, nutno predelat dle aktualni situace
+                               if (!strcmp(function,"New")) 
+                                       err = edg_wll_NotifNewServer(ctx,
+                                                               (edg_wll_QueryRec const * const *)conditions,
+                                                               address, notifId, &validity);
+                               else if (!strcmp(function,"Bind"))
+                                       err = edg_wll_NotifBindServer(ctx, notifId, address, &validity);
+                               else if (!strcmp(function,"Change"))
+                                       err = edg_wll_NotifChangeServer(ctx, notifId,
+                                                               (edg_wll_QueryRec const * const *)conditions, op);
+                               else if (!strcmp(function,"Refresh"))
+                                       err = edg_wll_NotifRefreshServer(ctx, notifId, &validity);
+                               else if (!strcmp(function,"Drop"))
+                                       err = edg_wll_NotifDropServer(ctx, notifId);
+                               
+                               switch (err) {
+                                       case 0: if (html) ret = HTTP_NOTIMPL;
+                                               else      ret = HTTP_OK; 
+                                               break;
+                                       case EEXIST: ret = HTTP_OK; break;
+                                       case EINVAL: ret = HTTP_INVALID; break;
+                                       case ENOENT: ret = HTTP_NOTFOUND; break;
+                                       case EPERM : ret = HTTP_UNAUTH; break;
+                                       case EDG_WLL_ERROR_NOINDEX: ret = HTTP_UNAUTH; break;
+                                       case ENOMEM: fatal = 1; ret = HTTP_INTERNAL; break;
+                                       default: ret = HTTP_INTERNAL; break;
+                               }
+                               /* glue errors (if eny) to XML responce */ 
+                               if (!html && !fatal)
+                                       if (edg_wll_NotifResultToXML(ctx, validity, &message))
+                                               ret = HTTP_INTERNAL;
+                       }
+                       
+                       free(function);
+                       free(address);
+                       edg_wll_NotifIdFree(notifId);
+                       if (conditions) {
+                               for (j = 0; conditions[j]; j++) {
+                                       for (i = 0; (conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                               edg_wll_QueryRecFree(&conditions[j][i]);
+                                       free(conditions[j]);
+                               }
+                               free(conditions);
+                       }
+               }
+                       
+        /* POST [something else]: not understood */
+               else ret = HTTP_BADREQ;
+
+/* other HTTP methods */
+       } else ret = HTTP_NOTALLOWED;
+
+err:   asprintf(response,"HTTP/1.1 %d %s",ret,edg_wll_HTTPErrorMessage(ret));
+       //(*headersOut) = malloc(2*sizeof(**headersOut));
+       //(*headersOut)[0] = strdup("Cache-Control: no-cache");
+       //(*headersOut)[1] = NULL;
+       *headersOut = (char **) response_headers;
+       if ((ret != HTTP_OK) && html)
+               *bodyOut = edg_wll_ErrorToHTML(ctx,ret);
+       else
+               *bodyOut = message;
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.server/src/lb_proto.h b/org.glite.lb.server/src/lb_proto.h
new file mode 100644 (file)
index 0000000..aea3957
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef _LB_PROTO_H
+#define _LB_PROTO_H
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+
+extern const char* const response_headers[];
+
+/* Handle a single request of the LB server protocol 
+ * returns a complete response string (may contain a formatted error
+ * message)
+ * or NULL on fatal error*/
+
+extern edg_wll_ErrorCode edg_wll_Proto(
+       edg_wll_Context,        /* INOUT: context */
+       char *,         /* IN: HTTP request line */
+       char **,        /* IN: HTTP message headers */
+       char *,         /* IN: HTTP message body */
+       char **,        /* OUT: HTTP response line */
+       char ***,       /* OUT: HTTP response headers */
+       char **         /* OUT: HTTP response body */
+);
+
+extern char *edg_wll_HTTPErrorMessage(int);
+
+#endif
diff --git a/org.glite.lb.server/src/lb_xml_parse.c.T b/org.glite.lb.server/src/lb_xml_parse.c.T
new file mode 100644 (file)
index 0000000..92fcefd
--- /dev/null
@@ -0,0 +1,1731 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <expat.h>
+
+#include "glite/wms/jobid/cjobid.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/escape.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+#include "glite/lb/xml_conversions.h"
+
+#include "glite/lb/purge.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/load.h"
+
+#include "lb_xml_parse.h"
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+#define QUERY_EVENTS_BEGIN     "<edg_wll_QueryEventsResult"
+#define QUERY_EVENTS_END       "</edg_wll_QueryEventsResult>\r\n"
+#define QUERY_JOBS_BEGIN       "<edg_wll_QueryJobsResult"
+#define QUERY_JOBS_END         "</edg_wll_QueryJobsResult>\r\n"
+#define PURGE_RESULT_BEGIN     "<edg_wll_PurgeResult"
+#define PURGE_RESULT_END       "</edg_wll_PurgeResult>\r\n"
+#define DUMP_RESULT_BEGIN      "<edg_wll_DumpResult"
+#define DUMP_RESULT_END                "</edg_wll_DumpResult>\r\n"
+#define LOAD_RESULT_BEGIN      "<edg_wll_LoadResult"
+#define LOAD_RESULT_END                "</edg_wll_LoadResult>\r\n"
+#define INDEXED_ATTRS_BEGIN    "<edg_wll_GetIndexedAttributesResult"
+#define INDEXED_ATTRS_END      "</edg_wll_GetIndexedAttributesResult>\r\n"
+#define NOTIF_RESULT_BEGIN     "<edg_wll_NotifResult"
+#define NOTIF_RESULT_END       "</edg_wll_NotifResult>\r\n"
+
+
+
+// XXX will be redundant soon
+#define USERJOBS_BEGIN         "<edg_wll_UserJobs"
+#define USERJOBS_END           "</edg_wll_UserJobs>\r\n"
+
+
+static char    *ops[] = { "equal","less","greater","within","unequal" },
+               *attrs[] = {    "jobid","owner","status","location","destination",
+                               "donecode","usertag","time","level","host","source",
+                               "instance","type","chkpt_tag", "resubmitted", "parent_job", "exitcode" };
+
+static const struct timeval null_timeval = {0,0};
+
+#define unexp() {\
+       char    *e;\
+\
+       if (XMLCtx->errtxt) {\
+               asprintf(&e,"%s\nunexpected <%s> at line %d",XMLCtx->errtxt,\
+                       el,XML_GetCurrentLineNumber(XMLCtx->p));\
+               free(XMLCtx->errtxt);\
+       } else asprintf(&e,"unexpected <%s> at line %d",\
+               el,XML_GetCurrentLineNumber(XMLCtx->p));\
+       XMLCtx->errtxt = e;\
+}
+
+
+
+static void startJobQueryRec(void *data, const char *el, const char **attr)
+{
+       unsigned int    i;
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"and")) unexp()
+                       break;
+               case 1: if (strcasecmp(el,"orJobConditions")) unexp()
+                       else {
+                               XMLCtx->position = -1;
+                               XMLCtx->job_conditions = realloc(XMLCtx->job_conditions,
+                                       (++XMLCtx->row+2)*sizeof(*XMLCtx->job_conditions));
+                               XMLCtx->job_conditions[XMLCtx->row] = NULL;
+                               XMLCtx->job_conditions[XMLCtx->row+1] = NULL;
+                       }
+                       break;
+               case 2:
+                       for (i=0; i<sizeof(ops)/sizeof(ops[0]); i++)
+                               if (!strcasecmp(el,ops[i])) break;
+                       if (i == sizeof(ops)/sizeof(ops[0])) unexp()
+                       else {
+                               if (!XMLCtx->job_conditions) break;
+
+                               /* malloc also terminator and set it to 0 (= EDG_WLL_QUERY_ATTR_UNDEF) */
+                               XMLCtx->job_conditions[XMLCtx->row] = realloc(XMLCtx->job_conditions[XMLCtx->row],
+                                       (++XMLCtx->position+2)*sizeof(**XMLCtx->job_conditions));
+                               memset(&XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position],0,2*sizeof(**XMLCtx->job_conditions));
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].op = i;
+                               XMLCtx->bound = 0;
+                       }
+                       break;
+               case 3: for (i=0; i<sizeof(attrs)/sizeof(attrs[0]) && 
+                               strcasecmp(el,attrs[i]); i++);
+                       if (i == sizeof(attrs)/sizeof(attrs[0])) unexp()
+                       else {
+                               if (!XMLCtx->job_conditions) break;
+                               if (!XMLCtx->job_conditions[XMLCtx->row]) break;
+
+                               if ( (i+1) == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                       if (strcmp(attr[0],"name")) { unexp() break;}
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr_id.tag = strdup(attr[1]);
+                               } 
+                               else if ( (i+1) == EDG_WLL_QUERY_ATTR_TIME) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                        if (attr[0] && strcmp(attr[0],"state")) { unexp() break;}
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr_id.state = edg_wll_StringToStat(attr[1]);
+                               }
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr = i+1;
+                       }               
+                       break;
+               default: unexp(); break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startQueryJobsRequest(void *data, const char *el, const char **attr)
+{
+       unsigned int    i;
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_QueryJobsRequest")) { unexp() break; }
+                       for ( i = 0; attr[i] && attr[i+1]; i += 2 )
+                       {
+                               if ( !strcmp(attr[i],"softLimit") )
+                                       XMLCtx->ctx->softLimit = atoi(attr[i+1]);
+                               else if ( !strcmp(attr[i],"queryRes") )
+                                       XMLCtx->ctx->p_query_results = atoi(attr[i+1]);
+                               else { unexp() break; }
+                       }
+                       break;
+               case 1: if (!strcasecmp(el,"and")) {
+                               XMLCtx->jobQueryRec_begin = XML_GetCurrentByteIndex(XMLCtx->p);
+                               break;
+                       }
+                       else if (!strcasecmp(el,"flags")) break;
+                       else unexp()
+                       break;
+               case 2: /* fall through */
+               case 3: /* do not check xml tags, processed in startJobQueryRec */
+               case 4: break;
+               default: unexp(); break;
+       }
+       XMLCtx->level++;
+}
+
+
+static void startQueryEventsRequest(void *data, const char *el, const char **attr UNUSED_VAR)
+{
+       unsigned int    i;
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_QueryEventsRequest")) { unexp() break; }
+                       for ( i = 0; attr[i] && attr[i+1]; i += 2 )
+                       {
+                               if ( !strcmp(attr[i],"softLimit") )
+                                       XMLCtx->ctx->softLimit = atoi(attr[i+1]);
+                               else if ( !strcmp(attr[i],"queryRes") )
+                                       XMLCtx->ctx->p_query_results = atoi(attr[i+1]);
+                               else { unexp() break; }
+                       }
+                       break;
+               case 1: if (strcasecmp(el,"and")) unexp()
+                       break;
+               case 2: if (!strcasecmp(el,"orJobConditions")) {
+                               XMLCtx->type = EDG_WLL_QUERY_TYPE_JOB_CONDITION;
+                               XMLCtx->position = -1;
+                                XMLCtx->job_conditions = realloc(XMLCtx->job_conditions,
+                                        (++XMLCtx->row+2)*sizeof(*XMLCtx->job_conditions));
+                                XMLCtx->job_conditions[XMLCtx->row] = NULL;
+                                XMLCtx->job_conditions[XMLCtx->row+1] = NULL;
+
+                       }
+                       else if (!strcasecmp(el,"orEventConditions")) {
+                               XMLCtx->type = EDG_WLL_QUERY_TYPE_EVENT_CONDITION;
+                               XMLCtx->position2 = -1;
+                                XMLCtx->event_conditions = realloc(XMLCtx->event_conditions,
+                                        (++XMLCtx->row2+2)*sizeof(*XMLCtx->event_conditions));
+                                XMLCtx->event_conditions[XMLCtx->row2] = NULL;
+                                XMLCtx->event_conditions[XMLCtx->row2+1] = NULL;
+                       }
+                       else unexp()
+                       break;
+               case 3:
+                       for (i=0; i<sizeof(ops)/sizeof(ops[0]); i++)
+                               if (!strcasecmp(el,ops[i])) break;
+                       if (i == sizeof(ops)/sizeof(ops[0])) unexp()
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_JOB_CONDITION) {
+                               if (!XMLCtx->job_conditions) break;
+
+                               /* malloc also terminator and set it to 0 (= EDG_WLL_QUERY_ATTR_UNDEF) */
+                               XMLCtx->job_conditions[XMLCtx->row] = realloc(XMLCtx->job_conditions[XMLCtx->row],
+                                       (++XMLCtx->position+2)*sizeof(**XMLCtx->job_conditions));
+                               memset(&XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position],0,2*sizeof(**XMLCtx->job_conditions));
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].op = i;
+                               XMLCtx->bound = 0;
+                       }
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_EVENT_CONDITION) {
+                               if (!XMLCtx->event_conditions) break;
+                               /* malloc also terminator and set it to 0 (= EDG_WLL_QUERY_ATTR_UNDEF) */
+                               XMLCtx->event_conditions[XMLCtx->row2] = realloc(XMLCtx->event_conditions[XMLCtx->row2],
+                                       (++XMLCtx->position2+2)*sizeof(**XMLCtx->event_conditions));
+                               memset(&XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2],0,2*sizeof(**XMLCtx->event_conditions));
+                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].op = i;
+                               XMLCtx->bound = 0;
+                       }
+                       
+                       break;
+               case 4: for (i=0; i<sizeof(attrs)/sizeof(attrs[0]) && 
+                               strcasecmp(el,attrs[i]); i++);
+                       if (i == sizeof(attrs)/sizeof(attrs[0])) unexp()
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_JOB_CONDITION) { 
+                               if (!XMLCtx->job_conditions[XMLCtx->row]) break;
+                               if ( (i+1) == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                       if (attr[0] && strcmp(attr[0],"name")) { unexp() break;}
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr_id.tag = strdup(attr[1]);
+                               } 
+                               else if ( (i+1) == EDG_WLL_QUERY_ATTR_TIME) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                        if (attr[0] && strcmp(attr[0],"state")) { unexp() break;}
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr_id.state = edg_wll_StringToStat(attr[1]);
+                                       printf("\nchecking time attr\n%s = %s (%d)\n\n", attr[0], attr[1], edg_wll_StringToStat(attr[1]));
+                               }
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr = i+1;
+                       }
+                       else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_EVENT_CONDITION) {
+                               if (!XMLCtx->event_conditions[XMLCtx->row2]) break;
+                               if ( (i+1) == EDG_WLL_QUERY_ATTR_USERTAG) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                       if (attr[0] && strcmp(attr[0],"name")) { unexp() break;}
+                                       XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position].attr_id.tag = strdup(attr[1]);
+                               } 
+                               else if ( (i+1) == EDG_WLL_QUERY_ATTR_TIME) {
+                                       if (!attr[0] || !attr[1]) { unexp() break;}
+                                        if (attr[0] && strcmp(attr[0],"state")) { unexp() break;}
+                                       XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position].attr_id.state = edg_wll_StringToStat(attr[1]);
+                               }
+                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].attr = i+1;
+                       }
+                       break;
+               default: unexp(); break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startPurgeRequest(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_PurgeRequest")) unexp()
+                       break;
+               case 1: if (strcasecmp(el,"jobs") && strcasecmp(el,"timeout")
+                               && strcasecmp(el,"flags")) unexp()
+                       else 
+                               XMLCtx->position = 0;
+                       break;
+               case 2: if (!strcasecmp(el,"jobId")) {
+                               XMLCtx->purgeRequestGlobal.jobs = realloc(XMLCtx->purgeRequestGlobal.jobs,
+                                               (XMLCtx->position+2) * sizeof(XMLCtx->purgeRequestGlobal.jobs));
+
+                                if (!XMLCtx->purgeRequestGlobal.jobs) { 
+                                       edg_wll_SetError(XMLCtx->ctx, ENOMEM, NULL); 
+                                       unexp() return;
+                               }
+                                XMLCtx->purgeRequestGlobal.jobs[XMLCtx->position+1] = NULL;
+                       }
+                       else if (XMLCtx->tagToIndex(el) >= 0 ) {
+                               /* static array, no need to initialize & allocate anything */
+                       }
+                       else 
+                               unexp()
+                       break;
+               default: unexp() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+
+static void startDumpRequest(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_DumpRequest")) unexp()
+                       break;
+               case 1: if (strcasecmp(el,"from") && strcasecmp(el,"to")) unexp()
+                       break;
+               default: unexp() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startLoadRequest(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (strcasecmp(el,"edg_wll_LoadRequest")) unexp()
+                       break;
+               case 1: if (strcasecmp(el,"server_file")) unexp()
+                       break;
+               default: unexp() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+
+
+static void startNotifRequest(void *data, const char *el, const char **attr)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+       
+       strcpy(XMLCtx->element, el);
+
+       switch (XMLCtx->level) {
+               case 0: if (!strcasecmp(el,"edg_wll_NotifRequest") && attr[0] && attr[1]) {
+                               if (strcmp(attr[0],"function")) { unexp() break;}
+                               else XMLCtx->notifFunction = strdup(attr[1]);
+                       }
+                       else unexp()
+                       break;
+               case 1: if (!strcasecmp(el,"and")) {
+                               XMLCtx->jobQueryRec_begin = XML_GetCurrentByteIndex(XMLCtx->p);
+                       }
+                       else if ( (strcasecmp(el,"notifId")) && (strcasecmp(el,"clientAddress")) && 
+                               (strcasecmp(el,"notifChangeOp")) ) unexp()
+                       break;
+               case 2: /* fall through */
+               case 3: /* do not check xml tags, processed in startJobQueryRec */
+               case 4: break;
+               default: unexp() 
+                        break;
+       }
+       XMLCtx->level++;
+}
+
+#undef unexp
+
+
+static void char_handler(void *data, const char *s, int len)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        int i, found = -1, temp_len1;
+       char *temp_s, *temp_s1;
+
+
+       /* if date are only spaces, t\, \r, \n ... don't bother with them */
+        for (i=0; i<len; i++)  
+                       if (!isspace(s[i])) { found = i; break; }
+        if (found == -1) return;
+
+       temp_s = malloc(len+1);
+
+       /* otherwise use them */
+       memcpy(temp_s,s,len);
+       temp_s[len] = 0;        
+       temp_s1 = edg_wll_UnescapeXML((const char *) temp_s);
+       temp_len1 = strlen(temp_s1);
+
+        if (XMLCtx->char_buf_len) XMLCtx->char_buf =
+               realloc(XMLCtx->char_buf,XMLCtx->char_buf_len+temp_len1 + 1);
+        else XMLCtx->char_buf = malloc(temp_len1 + 1);
+
+        memcpy(XMLCtx->char_buf+XMLCtx->char_buf_len,temp_s1,temp_len1 + 1);
+        XMLCtx->char_buf_len += temp_len1;
+       free(temp_s1);
+       free(temp_s);
+}
+
+
+
+static void endJobQueryRec(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       char *e;
+
+
+       if (XMLCtx->level == 4 &&
+               XMLCtx->job_conditions != NULL &&
+               XMLCtx->job_conditions[XMLCtx->row] != NULL) {
+               switch (XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr) {
+                       case EDG_WLL_QUERY_ATTR_JOBID:
+                       case EDG_WLL_QUERY_ATTR_PARENT:
+                               if ( (XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.j = 
+                                       edg_wll_from_string_to_jobid(XMLCtx)) == NULL )
+                               {
+                                       if (XMLCtx->errtxt) {
+                                               asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                                       XMLCtx->errtxt, XMLCtx->char_buf,
+                                                       XML_GetCurrentLineNumber(XMLCtx->p));
+                                               free(XMLCtx->errtxt);
+                                       } else asprintf(&e,"%s: invalid JobId at line %d",
+                                               XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                       XMLCtx->errtxt = e;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_OWNER:
+                       // XXX - this is way how to pass NULL, user will be extracted from ssl partner later
+                               if (XMLCtx->char_buf != NULL && !strcmp(XMLCtx->char_buf,"NULL")) {  
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.c = NULL; 
+                                       break; 
+                               }
+                               else /* fall through */
+                       case EDG_WLL_QUERY_ATTR_LOCATION:
+                       case EDG_WLL_QUERY_ATTR_DESTINATION:
+                       case EDG_WLL_QUERY_ATTR_USERTAG:
+                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.c = 
+                                       edg_wll_from_string_to_string(XMLCtx);
+                               break;
+                       case EDG_WLL_QUERY_ATTR_STATUS:
+                               if ( !XMLCtx->bound )
+                               {
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.i = 
+                                               edg_wll_from_string_to_edg_wll_JobStatCode(XMLCtx);
+                                       XMLCtx->bound++;
+                               }
+                               else
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.i = 
+                                               edg_wll_from_string_to_edg_wll_JobStatCode(XMLCtx);
+
+                               break;
+                       case EDG_WLL_QUERY_ATTR_DONECODE:
+                       case EDG_WLL_QUERY_ATTR_EXITCODE:
+                       case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                               if ( !XMLCtx->bound )
+                               {
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.i = 
+                                               edg_wll_from_string_to_int(XMLCtx);
+                                       XMLCtx->bound++;
+                               }
+                               else
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.i = 
+                                               edg_wll_from_string_to_int(XMLCtx);
+
+                               break;
+                       case EDG_WLL_QUERY_ATTR_TIME:
+                               if ( !XMLCtx->bound )
+                               {
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.t.tv_sec = 
+                                               edg_wll_from_string_to_time_t(XMLCtx);
+                                       XMLCtx->bound++;
+                               }
+                               else
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.t.tv_sec =
+                                               edg_wll_from_string_to_time_t(XMLCtx);
+                               break;
+                       default: 
+                               edg_wll_freeBuf(XMLCtx);
+                               XMLCtx->level--;
+                               if (XMLCtx->errtxt) {
+                                       asprintf(&e,"%s\n%s: invalid attribute type at line %d",
+                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                       free(XMLCtx->errtxt);
+                               } else asprintf(&e,"%s: invalid attribute type at line %d",
+                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                               XMLCtx->errtxt = e;
+                               break;
+               }
+       }
+        XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+static void endQueryJobsRequest(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+
+
+       if (XMLCtx->level == 2) {
+               if (!strcmp(XMLCtx->element,"flags") && XMLCtx->char_buf) {
+                       // XXX: check if it works
+                        XMLCtx->flags = edg_wll_string_to_stat_flags(XMLCtx->char_buf);
+               }
+               else if (!strcmp(el,"and")) {
+                       long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                                       -  XMLCtx->jobQueryRec_begin;
+
+                       parseJobQueryRec(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->jobQueryRec_begin, len,
+                               &XMLCtx->job_conditions);
+               }
+       }
+        XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+static void endQueryEventsRequest(void *data, const char *el UNUSED_VAR)
+{
+       edg_wll_XML_ctx *XMLCtx = data;
+       char *e;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"orJobConditions")) {
+                        /* make end-of-row */
+                        XMLCtx->job_conditions[XMLCtx->row] = realloc(XMLCtx->job_conditions[XMLCtx->row],
+                                (++XMLCtx->position+1)*sizeof(**XMLCtx->job_conditions));
+                        memset(&XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position],0,sizeof(**XMLCtx->job_conditions));
+                }
+               else if (!strcmp(XMLCtx->element,"orEventConditions")) {
+                        /* make end-of-row */
+                        XMLCtx->event_conditions[XMLCtx->row2] = realloc(XMLCtx->event_conditions[XMLCtx->row2],
+                                (++XMLCtx->position2+1)*sizeof(**XMLCtx->event_conditions));
+                        memset(&XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2],0,sizeof(**XMLCtx->event_conditions));
+                }
+       }
+       else if (XMLCtx->level == 5) {
+               if (XMLCtx->type == EDG_WLL_QUERY_TYPE_JOB_CONDITION &&
+                       XMLCtx->job_conditions != NULL &&
+                       XMLCtx->job_conditions[XMLCtx->row] !=NULL) {
+                       switch (XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].attr) {
+                               case EDG_WLL_QUERY_ATTR_JOBID:
+                               case EDG_WLL_QUERY_ATTR_PARENT:
+                                       if ( (XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.j = 
+                                               edg_wll_from_string_to_jobid(XMLCtx)) == NULL )
+                                       {
+                                               if (XMLCtx->errtxt) {
+                                                       asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                                       free(XMLCtx->errtxt);
+                                               } else asprintf(&e,"%s: invalid JobId at line %d",
+                                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                               XMLCtx->errtxt = e;
+                                       }
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_OWNER:
+                               case EDG_WLL_QUERY_ATTR_LOCATION:
+                               case EDG_WLL_QUERY_ATTR_DESTINATION:
+                               case EDG_WLL_QUERY_ATTR_USERTAG:
+                                       XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.c = 
+                                               edg_wll_from_string_to_string(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_STATUS:
+                               case EDG_WLL_QUERY_ATTR_DONECODE:
+                               case EDG_WLL_QUERY_ATTR_EXITCODE:
+                               case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.i = 
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.i = 
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_TIME:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value.t.tv_sec = 
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->job_conditions[XMLCtx->row][XMLCtx->position].value2.t.tv_sec =
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                       break;
+                               default: 
+                                       edg_wll_freeBuf(XMLCtx);
+                                       XMLCtx->level--;
+                                       if (XMLCtx->errtxt) {
+                                               asprintf(&e,"%s\n%s: invalid attribute type at line %d",
+                                                       XMLCtx->errtxt, XMLCtx->char_buf,
+                                                       XML_GetCurrentLineNumber(XMLCtx->p));
+                                               free(XMLCtx->errtxt);
+                                       } else asprintf(&e,"%s: invalid attribute type at line %d",
+                                               XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                       XMLCtx->errtxt = e;
+                                       break;
+                       }
+               }
+               else if (XMLCtx->type == EDG_WLL_QUERY_TYPE_EVENT_CONDITION &&
+                       XMLCtx->event_conditions != NULL &&
+                       XMLCtx->event_conditions[XMLCtx->row2] != NULL) {
+                       switch (XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].attr) {
+                               case EDG_WLL_QUERY_ATTR_TIME:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value.t.tv_sec = 
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value2.t.tv_sec =
+                                                       edg_wll_from_string_to_time_t(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_LEVEL:
+                               case EDG_WLL_QUERY_ATTR_SOURCE:
+                               case EDG_WLL_QUERY_ATTR_EVENT_TYPE:
+                                       if ( !XMLCtx->bound )
+                                       {
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value.i =
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                               XMLCtx->bound++;
+                                       }
+                                       else
+                                               XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value2.i =
+                                                       edg_wll_from_string_to_int(XMLCtx);
+                                       break;
+                               case EDG_WLL_QUERY_ATTR_HOST:
+                               case EDG_WLL_QUERY_ATTR_INSTANCE:
+                               case EDG_WLL_QUERY_ATTR_USERTAG:
+                                       XMLCtx->event_conditions[XMLCtx->row2][XMLCtx->position2].value.c =
+                                               edg_wll_from_string_to_string(XMLCtx);
+                                       break;
+                               default: 
+                                       edg_wll_freeBuf(XMLCtx);
+                                       XMLCtx->level--;
+                                       if (XMLCtx->errtxt) {
+                                               asprintf(&e,"%s\n%s: invalid attribute type at line %d",
+                                                       XMLCtx->errtxt, XMLCtx->char_buf,
+                                                       XML_GetCurrentLineNumber(XMLCtx->p));
+                                               free(XMLCtx->errtxt);
+                                       } else asprintf(&e,"%s: invalid attribute type at line %d",
+                                               XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                                       XMLCtx->errtxt = e;
+                                       break;
+                       }
+               } 
+       
+               XMLCtx->char_buf = NULL;
+               XMLCtx->char_buf_len = 0;
+       }
+       XMLCtx->level--;
+}
+
+
+static void endPurgeRequest(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+        char *e;
+       int index;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"flags")) 
+                       XMLCtx->purgeRequestGlobal.flags = edg_wll_string_to_purge_flags(XMLCtx->char_buf);
+       }
+       else if (XMLCtx->level == 3) {
+               if (!strcmp(XMLCtx->element,"jobId") && XMLCtx->purgeRequestGlobal.jobs != NULL) {
+                       if ( (XMLCtx->purgeRequestGlobal.jobs[XMLCtx->position++] = 
+                               edg_wll_from_string_to_string(XMLCtx)) == NULL )
+                       {
+                               if (XMLCtx->errtxt) {
+                                       asprintf(&e,"%s\n%s: invalid JobId at line %d",
+                                               XMLCtx->errtxt, XMLCtx->char_buf,
+                                               XML_GetCurrentLineNumber(XMLCtx->p));
+                                       free(XMLCtx->errtxt);
+                               } else asprintf(&e,"%s: invalid JobId at line %d",
+                                       XMLCtx->char_buf,XML_GetCurrentLineNumber(XMLCtx->p));
+                               XMLCtx->errtxt = e;
+                       }
+               }
+               else if ( ((index = XMLCtx->tagToIndex(XMLCtx->element)) >= 0 ) && XMLCtx->char_buf ) {
+                       XMLCtx->purgeRequestGlobal.timeout[index] = 
+                               edg_wll_from_string_to_time_t(XMLCtx);
+               }
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+static void endDumpRequest(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"from")) {
+                       if (isdigit(XMLCtx->char_buf[0]))
+                       XMLCtx->dumpRequestGlobal.from = edg_wll_from_string_to_time_t(XMLCtx);
+                       else
+                               XMLCtx->dumpRequestGlobal.from = edg_wll_StringToDumpConst(XMLCtx->char_buf);
+               }
+               else if (!strcmp(XMLCtx->element,"to")) {
+                       if (isdigit(XMLCtx->char_buf[0]))
+                        XMLCtx->dumpRequestGlobal.to = edg_wll_from_string_to_time_t(XMLCtx);
+                       else
+                               XMLCtx->dumpRequestGlobal.to = edg_wll_StringToDumpConst(XMLCtx->char_buf);
+               }
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+static void endLoadRequest(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"server_file")) 
+                       XMLCtx->loadRequestGlobal.server_file = edg_wll_from_string_to_string(XMLCtx);
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+static void endNotifRequest(void *data, const char *el UNUSED_VAR)
+{
+        edg_wll_XML_ctx *XMLCtx = data;
+       char *pom;
+
+        if (XMLCtx->level == 2) {
+                if (!strcmp(XMLCtx->element,"notifId")) {
+                       pom = edg_wll_from_string_to_string(XMLCtx);
+                       edg_wll_NotifIdParse(pom, &XMLCtx->notifId);
+                       free(pom);
+               }
+               else if (!strcmp(XMLCtx->element,"clientAddress")) {
+                       XMLCtx->notifClientAddress = edg_wll_from_string_to_string(XMLCtx);
+               }
+               else if (!strcmp(XMLCtx->element,"notifChangeOp")) {
+                       pom = edg_wll_from_string_to_string(XMLCtx);
+                       XMLCtx->notifChangeOp = edg_wll_StringToNotifChangeOp(pom);
+                       free(pom);
+               }
+               else if (!strcmp(el,"and")) {
+                       long len = (XML_GetCurrentByteIndex(XMLCtx->p) + XML_GetCurrentByteCount(XMLCtx->p))
+                                       -  XMLCtx->jobQueryRec_begin;
+       
+                       parseJobQueryRec(XMLCtx->ctx, XMLCtx->message_body + XMLCtx->jobQueryRec_begin, len,
+                               &XMLCtx->job_conditions);
+               }
+       }
+
+       XMLCtx->char_buf = NULL;
+        XMLCtx->char_buf_len = 0;
+       XMLCtx->level--;
+}
+
+
+
+int parseJobQueryRec(edg_wll_Context ctx, const char *messageBody, long len, edg_wll_QueryRec ***conditions)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.row = -1;
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startJobQueryRec, endJobQueryRec);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, len, 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i,j;
+
+               if (XMLCtx.job_conditions) {
+                       for (j = 0; XMLCtx.job_conditions[j]; j++) {
+                               for (i = 0; (XMLCtx.job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.job_conditions[j][i]);
+                               free(XMLCtx.job_conditions[j]);
+                       }
+                       free(XMLCtx.job_conditions);
+               }
+
+               /* empty list terminators */
+               conditions[0] = NULL;
+       } else {
+               *conditions = XMLCtx.job_conditions;
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+/* parse queryJobs request from client */
+int parseQueryJobsRequest(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***conditions, int *flags)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       XMLCtx.message_body = messageBody;
+       XMLCtx.position = 0;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startQueryJobsRequest, endQueryJobsRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i,j;
+
+               if (XMLCtx.job_conditions) {
+                       for (j = 0; XMLCtx.job_conditions[j]; j++) {
+                               for (i = 0; (XMLCtx.job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.job_conditions[j][i]);
+                               free(XMLCtx.job_conditions[j]);
+                       }
+                       free(XMLCtx.job_conditions);
+               }
+
+               /* empty list terminators */
+               conditions[0] = NULL;
+               *flags = 0;
+       } else {
+               *conditions = XMLCtx.job_conditions;
+               *flags = XMLCtx.flags;
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+/* parse queryEvents request from client */
+int parseQueryEventsRequest(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***job_conditions, edg_wll_QueryRec ***event_conditions)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.row = -1;
+       XMLCtx.row2 = -1;
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startQueryEventsRequest, endQueryEventsRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i,j;
+
+               if (XMLCtx.job_conditions) {
+                       for (j = 0; XMLCtx.job_conditions[j]; j++) {
+                               for (i = 0 ; (XMLCtx.job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.job_conditions[j][i]);
+                               free(XMLCtx.job_conditions[j]);
+                       }
+                       free(XMLCtx.job_conditions);
+               }
+               if (XMLCtx.event_conditions) {
+                       for (j = 0; XMLCtx.event_conditions[j]; j++) {
+                               for (i = 0 ; (XMLCtx.event_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.event_conditions[j][i]);
+                               free(XMLCtx.event_conditions[j]);
+                       }
+                       free(XMLCtx.event_conditions);
+               }
+
+               /* empty list terminators */
+               job_conditions[0] = NULL;
+               event_conditions[0] = NULL;
+       } else {
+               *job_conditions = XMLCtx.job_conditions;
+               *event_conditions = XMLCtx.event_conditions;
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+
+/* parse purge request from client */
+int parsePurgeRequest(edg_wll_Context ctx, char *messageBody, int (*tagToIndex)(), edg_wll_PurgeRequest *request)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+       XMLCtx.tagToIndex = tagToIndex;
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startPurgeRequest, endPurgeRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i;
+
+               if (XMLCtx.purgeRequestGlobal.jobs) {
+                       for (i=0; XMLCtx.purgeRequestGlobal.jobs[i]; i++) 
+                               free(XMLCtx.purgeRequestGlobal.jobs[i]);
+                       free(XMLCtx.purgeRequestGlobal.jobs);
+               }
+               memset(request,0,sizeof(*request));
+               
+       } else {
+               memcpy(request, &XMLCtx.purgeRequestGlobal, sizeof(XMLCtx.purgeRequestGlobal));
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+
+/* parse dump request from client */
+int parseDumpRequest(edg_wll_Context ctx, char *messageBody, edg_wll_DumpRequest *request)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startDumpRequest, endDumpRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) 
+               memset(request,0,sizeof(*request));
+               
+       else {
+               memcpy(request, &XMLCtx.dumpRequestGlobal, sizeof(XMLCtx.dumpRequestGlobal));
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+/* parse load request from client */
+int parseLoadRequest(edg_wll_Context ctx, char *messageBody, edg_wll_LoadRequest *request)
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startLoadRequest, endLoadRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) 
+               memset(request,0,sizeof(*request));
+               
+       else {
+               memcpy(request, &XMLCtx.loadRequestGlobal, sizeof(XMLCtx.loadRequestGlobal));
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+
+/* parse Notif request from client */
+int parseNotifRequest(edg_wll_Context ctx, char *messageBody, char **function, edg_wll_NotifId *notifId, char **address, edg_wll_NotifChangeOp *op, edg_wll_QueryRec ***conditions) 
+{
+       int     ret;
+       edg_wll_XML_ctx         XMLCtx;
+       XML_Char *encoding = "ISO-8859-1";
+
+       
+       /* returns emty variables as default; only some variables will be filled in */
+       /* depending on vaule of XMLCtx.notifFunction */
+       *function = NULL;
+       *notifId = NULL;
+       *address = NULL;
+       *op = EDG_WLL_NOTIF_NOOP;
+       *conditions = NULL;
+
+       errno = 0;
+       edg_wll_initXMLCtx(&XMLCtx);
+       XMLCtx.ctx = ctx;
+       XMLCtx.message_body = messageBody;
+       edg_wll_ResetError(ctx);
+
+
+        /* initialize parser */
+        XMLCtx.p = XML_ParserCreate(encoding);
+        XML_SetElementHandler(XMLCtx.p, startNotifRequest, endNotifRequest);
+        XML_SetCharacterDataHandler(XMLCtx.p, char_handler);
+        XML_SetUserData(XMLCtx.p, (void *) &XMLCtx);
+
+
+        if (! XML_Parse(XMLCtx.p, messageBody, strlen(messageBody), 1)) {
+                char *errorMessage;
+
+                asprintf(&errorMessage, "Parse error at line %d:\n%s\n",
+                        XML_GetCurrentLineNumber(XMLCtx.p),
+                        XML_ErrorString(XML_GetErrorCode(XMLCtx.p)));
+
+                edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, errorMessage);
+                free(errorMessage);
+        } else if (XMLCtx.errtxt) edg_wll_SetError(ctx, EDG_WLL_ERROR_XML_PARSE, XMLCtx.errtxt);
+
+
+       if ((ret = edg_wll_Error(ctx,NULL,NULL))) {
+               int i,j;
+
+               if (XMLCtx.job_conditions) {
+                       for (j = 0; XMLCtx.job_conditions[j]; j++) {
+                               for (i = 0; (XMLCtx.job_conditions[j][i].attr != EDG_WLL_QUERY_ATTR_UNDEF); i++ )
+                                       edg_wll_QueryRecFree(&XMLCtx.job_conditions[j][i]);
+                               free(XMLCtx.job_conditions[j]);
+                       }
+                       free(XMLCtx.job_conditions);
+               }
+               free(XMLCtx.notifFunction);
+               edg_wll_NotifIdFree(&XMLCtx.notifId);
+               free(XMLCtx.notifClientAddress);
+
+               *function = NULL;
+               *notifId = NULL;
+               *address = NULL;
+               *op = EDG_WLL_NOTIF_NOOP;
+               *conditions = NULL;
+       } else {
+               *function = XMLCtx.notifFunction;
+               *notifId = XMLCtx.notifId;
+               *address = XMLCtx.notifClientAddress;
+               *op = XMLCtx.notifChangeOp;
+               *conditions = XMLCtx.job_conditions;
+       }
+
+       
+        XML_ParserFree(XMLCtx.p);
+       edg_wll_freeXMLCtx(&XMLCtx);
+       return ret;
+}
+
+
+int edg_wll_QueryEventsToXML(edg_wll_Context ctx, edg_wll_Event *eventsOut, char **message)
+{
+        char *pomA, *pomB;
+       char **list = NULL;
+        int i = 0, len, tot_len = 0;
+       int *len_list = NULL;
+
+
+        while (eventsOut && eventsOut[i].any.type != EDG_WLL_EVENT_UNDEF) {
+               pomB = edg_wll_EventToString(eventsOut[i].any.type);
+                trio_asprintf(&pomA,"      <edg_wll_Event name=\"%|Xs\">\r\n", pomB);
+               free(pomB);
+               pomB = pomA;
+
+
+@@@{
+        selectType $event '_common_';
+        for (getFieldsOrdered $event) {
+                my $f = selectField $event $_;
+                my $ft = $f->{type};
+               my $n = $f->{null};
+                gen "\tedg_wll_add_$ft\_to_XMLBody(&pomB, eventsOut[i].any.$_, \"$_\", $n);\n";
+        }
+
+        gen "\tswitch (eventsOut[i].any.type) {\n";
+        for my $t (sort { $event->{order}->{$a} <=> $event->{order}->{$b} } getTypes $event) {
+                if ($t ne 'any') {
+                        selectType $event $t;
+                        my $u = uc $t;
+                        gen "\t    case EDG_WLL_EVENT_$u :\n";
+                        for (getFieldsOrdered $event) {
+                                my $f = selectField $event $_;
+                                my $ft = $f->{type};
+                               my $n = $f->{null};
+                               $t = lcfirst $t;
+                                gen "\t\tedg_wll_add_$ft\_to_XMLBody(&pomB, eventsOut[i].$t.$_, \"$_\", $n);\n";
+                        }
+                       gen "\t\tbreak;\n";
+               }
+        }
+        gen "\t    default : break;\n";
+        gen "\t}\n";
+@@@}
+
+
+
+                len = asprintf(&pomA,"%s      </edg_wll_Event>\r\n", pomB);
+                free(pomB);
+                i++;
+               tot_len += len;
+
+               list      = (char **) realloc(list, i * sizeof(*list));
+               list[i-1] = pomA;
+               len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+               len_list[i-1] = len;
+               
+        }
+
+       /* list termination */
+       list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+       /* test errors */
+       if (ctx->errDesc || ctx->errCode)
+               len = trio_asprintf(&pomB," code=\"%d\" desc=\"%|Xs\">\r\n",ctx->errCode,ctx->errDesc);
+       else
+               len = asprintf(&pomB,">\r\n");
+
+       /* glueing all list fields together & freeing the list */
+       pomA = (char *) malloc(tot_len  * sizeof(char) + 
+                               sizeof(QUERY_EVENTS_BEGIN) + len + sizeof(QUERY_EVENTS_END) - 1);
+       
+       memcpy(pomA, QUERY_EVENTS_BEGIN, sizeof(QUERY_EVENTS_BEGIN));
+       memcpy(pomA + sizeof(QUERY_EVENTS_BEGIN) - 1, pomB, len);
+       free(pomB);
+       pomB = pomA + sizeof(QUERY_EVENTS_BEGIN) + len - 1;     
+
+       i = 0;
+       while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+               pomB += len_list[i];
+
+               /* freeing the list */
+               free(list[i]);
+
+                i++;
+        }
+
+       strcpy(pomB, QUERY_EVENTS_END);
+
+
+       free(list);
+       free(len_list);
+
+
+        *message = pomA;
+
+        return 0;
+}
+
+int edg_wll_QueryJobsToXML(edg_wll_Context ctx, edg_wlc_JobId *jobsIn, edg_wll_JobStat *statesIn, char **message) 
+{
+        char *pomA, *pomB, *pomC;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0, nres = 0;
+        int *len_list = NULL;
+
+       
+       if (jobsIn) for (nres = 0; jobsIn[nres]; nres++);
+       else if (statesIn) for (nres = 0; statesIn[nres].state; nres++);
+
+        while (i < nres) {
+                trio_asprintf(&pomA,"\t<edg_wll_Job>\r\n");
+                pomB = pomA;
+               
+               if (jobsIn) {
+                       trio_asprintf(&pomA,"%s\t\t<jobId>%|Xs</jobId>\r\n",
+                               pomB, pomC=edg_wlc_JobIdUnparse(jobsIn[i]));
+                       free(pomC);
+                       free(pomB);
+                       pomB = pomA;
+               }
+
+               if (statesIn) {
+                       edg_wll_JobStatusToXML(ctx, statesIn[i], &pomC);        
+                       trio_asprintf(&pomA,"%s\t\t%s",pomB,pomC);
+               }
+               else {
+                       pomC = edg_wll_StatToString(EDG_WLL_JOB_UNKNOWN);
+                       trio_asprintf(&pomA,"%s\t\t<jobStat name=\"%|Xs\">\r\n\r\n\t\t</jobStat>\r\n",
+                               pomB, pomC);
+               }
+
+               free(pomB);
+               free(pomC);
+               pomB = pomA;
+       
+
+                len = asprintf(&pomA,"%s\t</edg_wll_Job>\r\n", pomB);
+                free(pomB);
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;   
+       }
+       
+       /* list termination */
+       list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* test errors */
+        if (ctx->errDesc || ctx->errCode)
+                len = trio_asprintf(&pomB," code=\"%d\" desc=\"%|Xs\">\r\n",ctx->errCode,ctx->errDesc);
+        else
+                len = asprintf(&pomB,">\r\n");
+
+       /* glueing all list fields together & freeing the list */
+       pomA = (char *) malloc(tot_len  * sizeof(char) + 
+                               sizeof(QUERY_JOBS_BEGIN) + len + sizeof(QUERY_JOBS_END) - 1);
+       
+       memcpy(pomA, QUERY_JOBS_BEGIN, sizeof(QUERY_JOBS_BEGIN));       
+        memcpy((pomA + sizeof(QUERY_JOBS_BEGIN) - 1), pomB, len);
+        free(pomB);
+       pomB = pomA + sizeof(QUERY_JOBS_BEGIN) + len - 1;       
+
+       i = 0;
+       while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+               pomB += len_list[i];
+
+               /* freeing the list */
+               free(list[i]);
+
+                i++;
+        }
+
+       strcpy(pomB, QUERY_JOBS_END);
+
+
+       free(list);
+       free(len_list);
+
+
+        *message = pomA;
+
+       return 0;
+}
+
+/* construct Message-Body of Response-Line for edg_wll_UserJobs */
+int edg_wll_UserJobsToXML(edg_wll_Context ctx, edg_wlc_JobId *jobsOut, char **message)
+{
+        char *pomA, *pomB;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0;
+        int *len_list = NULL;
+
+
+        while (jobsOut[i]) {
+               len = trio_asprintf(&pomA,"      <jobId>%|Xs</jobId>\r\n",
+                       pomB=edg_wlc_JobIdUnparse(jobsOut[i]));
+
+                free(pomB);
+
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+               pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* test errors */
+        if (ctx->errDesc || ctx->errCode)
+                len = trio_asprintf(&pomB," code=\"%d\" desc=\"%|Xs\">\r\n",ctx->errCode,ctx->errDesc);
+        else
+                len = asprintf(&pomB,">\r\n");
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) +
+                                sizeof(USERJOBS_BEGIN) + len + sizeof(USERJOBS_END) - 1);
+
+        memcpy(pomA, USERJOBS_BEGIN, sizeof(USERJOBS_BEGIN));
+        memcpy((pomA + sizeof(USERJOBS_BEGIN) - 1), pomB, len);
+        free(pomB);
+        pomB = pomA + sizeof(USERJOBS_BEGIN) + len - 1;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+
+        strcpy(pomB, USERJOBS_END);
+
+
+        free(list);
+        free(len_list);
+
+
+        *message = pomA;
+
+        return 0;
+}
+
+void edg_wll_add_stslist_to_XMLBody(edg_wll_Context ctx, char **body, const edg_wll_JobStat *toAdd, const char *tag, const char *UNUSED_subTag, const int null)
+{
+        char *pomA, *pomB, *newBody;
+        char **list = NULL;
+        int i = 0, len, tot_len = 0;
+        int *len_list = NULL;
+
+
+        while (toAdd[i].state != null) {
+                edg_wll_JobStatusToXML(ctx, toAdd[i], &pomA);
+                len = strlen(pomA);
+
+                i++;
+                tot_len += len;
+
+                list      = (char **) realloc(list, i * sizeof(*list));
+                list[i-1] = pomA;
+                pomA = NULL;
+                len_list      = (int *) realloc(len_list, i * sizeof(*len_list));
+                len_list[i-1] = len;
+        }
+
+        /* list termination */
+        list = (char **) realloc(list, (i+1) * sizeof(*list));
+        list[i] = NULL;
+
+        /* glueing all list fields together & freeing the list */
+        pomA = (char *) malloc(tot_len  * sizeof(char) + 1);
+       pomB = pomA;
+
+        i = 0;
+        while (list[i]) {
+                memcpy(pomB, list[i], len_list[i] );
+                pomB += len_list[i];
+
+                /* freeing the list */
+                free(list[i]);
+
+                i++;
+        }
+       *pomB = '\0';
+        free(list);
+        free(len_list);
+
+        asprintf(&newBody,"%s\t\t\t<%s>\r\n%s\t\t\t</%s>\r\n", *body, tag, pomA, tag);
+        free(*body);
+        free(pomA);
+        *body = newBody;
+}
+
+
+
+/* construct Message-Body of Response-Line for edg_wll_JobStatus */
+int edg_wll_JobStatusToXML(edg_wll_Context ctx, edg_wll_JobStat stat, char **message)
+{
+        char *pomA, *pomB, *pomC;
+
+       
+       pomB = strdup("");
+
+@@@{
+       selectType $status '_common_';
+        for (getFieldsOrdered $status) {
+                my $f = selectField $status $_;
+               next if defined($f->{special}) && $f->{special} eq 'XMLstructured';
+                my $ft = $f->{type};
+               my $n = $f->{null};
+                gen "edg_wll_add_$ft\_to_XMLBody(&pomB, stat.$_, \"$_\", $n);\n";
+        }
+@@@}
+       if (stat.children) edg_wll_add_strlist_to_XMLBody(&pomB, stat.children, "children", "jobId", "\t\t\t", NULL);
+       if (stat.children_hist) edg_wll_add_intlist_to_XMLBody(&pomB, stat.children_hist, "children_hist", edg_wll_StatToString, "\t\t\t", 1, stat.children_hist[0]);
+       if (stat.children_states) edg_wll_add_stslist_to_XMLBody(ctx, &pomB, stat.children_states, "children_states", "", EDG_WLL_JOB_UNDEF);
+       if (stat.user_tags) edg_wll_add_taglist_to_XMLBody(&pomB, stat.user_tags, "user_tags", "tag", "name", "\t\t\t", NULL);
+       if (stat.stateEnterTimes) edg_wll_add_intlist_to_XMLBody(&pomB, stat.stateEnterTimes, "stateEnterTimes", edg_wll_StatToString, "\t\t\t",1, stat.stateEnterTimes[0]);
+
+       pomC = edg_wll_StatToString(stat.state);
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomA,"<jobStat name=\"%|Xs\" code=\"%d\" desc=\"%|Xs\">\r\n%s</jobStat>",
+                       pomC, ctx->errCode,ctx->errDesc, pomB);
+       else
+               trio_asprintf(&pomA,"<jobStat name=\"%|Xs\">\r\n%s</jobStat>",
+                       pomC, pomB);
+
+        free(pomB);
+       free(pomC);
+
+        *message = pomA;
+
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Purge */
+int edg_wll_PurgeResultToXML(
+                edg_wll_Context ctx,
+               edg_wll_PurgeResult *result,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!result) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       edg_wll_add_strlist_to_XMLBody(&pomA, result->jobs, "jobs", "jobId", "\t", NULL);
+       edg_wll_add_string_to_XMLBody(&pomA, result->server_file, "server_file", NULL); 
+
+       if (ctx->errDesc || ctx->errCode)
+                trio_asprintf(&pomB,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       PURGE_RESULT_BEGIN, ctx->errCode, ctx->errDesc, pomA, PURGE_RESULT_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", PURGE_RESULT_BEGIN, pomA, PURGE_RESULT_END);
+       free(pomA);
+
+        *message = pomB;
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Dump */
+int edg_wll_DumpResultToXML(
+                edg_wll_Context ctx,
+               edg_wll_DumpResult *result,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!result) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       edg_wll_add_string_to_XMLBody(&pomA, result->server_file, "server_file", NULL);
+        if (result->from < 0)
+                edg_wll_add_string_to_XMLBody(&pomA,
+                        edg_wll_DumpConstToString(result->from), "from", NULL);
+        else
+       edg_wll_add_time_t_to_XMLBody(&pomA, result->from, "from", 0);
+        if (result->to < 0)
+                edg_wll_add_string_to_XMLBody(&pomA,
+                        edg_wll_DumpConstToString(result->to), "to", NULL);
+        else
+       edg_wll_add_time_t_to_XMLBody(&pomA, result->to, "to", 0);
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomB,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       DUMP_RESULT_BEGIN, ctx->errCode, ctx->errDesc, pomA, DUMP_RESULT_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", DUMP_RESULT_BEGIN, pomA, DUMP_RESULT_END);
+       free(pomA);
+
+        *message = pomB;
+        return 0;
+}
+
+
+/* construct Message-Body of Request-Line for edg_wll_Load */
+int edg_wll_LoadResultToXML(
+                edg_wll_Context ctx,
+               edg_wll_LoadResult *result,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       if (!result) { *message = NULL; return(-1); }
+
+       pomA = strdup("");
+       edg_wll_add_string_to_XMLBody(&pomA, result->server_file, "server_file", NULL);
+       edg_wll_add_time_t_to_XMLBody(&pomA, result->from, "from", 0);
+       edg_wll_add_time_t_to_XMLBody(&pomA, result->to, "to", 0);
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomB,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       LOAD_RESULT_BEGIN, ctx->errCode, ctx->errDesc, pomA, LOAD_RESULT_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", LOAD_RESULT_BEGIN, pomA, LOAD_RESULT_END);
+       free(pomA);
+
+        *message = pomB;
+        return 0;
+}
+
+
+/* construct Message-Body of Request-Line for edg_wll_GetIndexedAttrs */
+int edg_wll_IndexedAttrsToXML(
+                edg_wll_Context ctx,
+                char **message)
+{
+       int i,j;
+       char *pomA, *pomB;
+
+       pomA = strdup("");
+       if (ctx->job_index) {
+               for (i=0; ctx->job_index[i]; i++) {
+                       asprintf(&pomB, "%s\t%s\r\n",pomA,"<index>");
+                       free(pomA);
+                       pomA = pomB;
+
+                       for (j=0; ctx->job_index[i][j].attr != EDG_WLL_QUERY_ATTR_UNDEF; j++) {
+                               asprintf(&pomB, "%s\t\t%s\r\n",pomA,"<QueryRec>");
+                               free(pomA);
+                               pomA = pomB;
+
+                               edg_wll_add_string_to_XMLBody(&pomA, 
+                                       edg_wll_query_attrToString(ctx->job_index[i][j].attr), 
+                                       "attribute", NULL);
+
+                               if (ctx->job_index[i][j].attr == EDG_WLL_QUERY_ATTR_TIME)
+                                       edg_wll_add_string_to_XMLBody(&pomA,
+                                               edg_wll_StatToString(ctx->job_index[i][j].attr_id.state),
+                                               "state", NULL);
+
+                               if (ctx->job_index[i][j].attr == EDG_WLL_QUERY_ATTR_USERTAG)
+                                        edg_wll_add_string_to_XMLBody(&pomA,
+                                               ctx->job_index[i][j].attr_id.tag,
+                                               "name", NULL);
+
+                               asprintf(&pomB, "%s\t\t%s\r\n",pomA,"</QueryRec>");
+                               free(pomA);
+                               pomA = pomB;
+                       }
+                       
+                       asprintf(&pomB, "%s\t%s\r\n",pomA,"</index>");
+                       free(pomA);
+                       pomA = pomB;
+               }
+       }
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomB,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       INDEXED_ATTRS_BEGIN, ctx->errCode, ctx->errDesc, pomA, INDEXED_ATTRS_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", INDEXED_ATTRS_BEGIN, pomA, INDEXED_ATTRS_END);
+       free(pomA);
+
+
+        *message = pomB;
+        return 0;
+}
+
+
+
+/* construct Message-Body of Request-Line for edg_wll_Notif */
+int edg_wll_NotifResultToXML(
+                edg_wll_Context ctx,
+               time_t validity,
+                char **message)
+{
+        char *pomA, *pomB;
+
+
+       pomA = strdup("");
+       edg_wll_add_time_t_to_XMLBody(&pomA, validity, "validity", -1);
+
+       if (ctx->errDesc || ctx->errCode)
+               trio_asprintf(&pomB,"%s code=\"%d\" desc=\"%|Xs\">\r\n%s%s",
+                       NOTIF_RESULT_BEGIN, ctx->errCode, ctx->errDesc, pomA, NOTIF_RESULT_END);
+       else
+               trio_asprintf(&pomB,"%s>\r\n%s%s", NOTIF_RESULT_BEGIN, pomA, NOTIF_RESULT_END);
+       free(pomA);
+
+        *message = pomB;
+        return 0;
+}
+
+
diff --git a/org.glite.lb.server/src/lb_xml_parse.h b/org.glite.lb.server/src/lb_xml_parse.h
new file mode 100644 (file)
index 0000000..51db4cf
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef _LB_XML_PARSE_H
+#define _LB_XML_PARSE_H
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/notification.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/load.h"
+
+/* function for parsing/unparsing XML requests from client */
+
+int parseJobQueryRec(edg_wll_Context ctx, const char *messageBody, long len, edg_wll_QueryRec ***conditions);
+int parseQueryJobsRequest(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***conditions, int *flags);
+int parseQueryEventsRequest(edg_wll_Context ctx, char *messageBody, edg_wll_QueryRec ***job_conditions, edg_wll_QueryRec ***event_conditions);
+int parsePurgeRequest(edg_wll_Context ctx, char *messageBody, int (*tagToIndex)(), edg_wll_PurgeRequest *request);
+int parseDumpRequest(edg_wll_Context ctx, char *messageBody, edg_wll_DumpRequest *request);
+int parseLoadRequest(edg_wll_Context ctx, char *messageBody, edg_wll_LoadRequest *request);
+int parseNotifRequest(edg_wll_Context ctx, char *messageBody, char **function, edg_wll_NotifId *notifId, char **address, edg_wll_NotifChangeOp *op, edg_wll_QueryRec ***conditions);
+int edg_wll_QueryEventsToXML(edg_wll_Context, edg_wll_Event *, char **);
+int edg_wll_QueryJobsToXML(edg_wll_Context, edg_wlc_JobId *, edg_wll_JobStat *, char **);
+int edg_wll_JobStatusToXML(edg_wll_Context, edg_wll_JobStat, char **);
+int edg_wll_UserJobsToXML(edg_wll_Context, edg_wlc_JobId *, char **);
+int edg_wll_PurgeResultToXML(edg_wll_Context ctx, edg_wll_PurgeResult *result, char **message);
+int edg_wll_DumpResultToXML(edg_wll_Context ctx, edg_wll_DumpResult *result, char **message);
+int edg_wll_LoadResultToXML(edg_wll_Context ctx, edg_wll_LoadResult *result, char **message);
+int edg_wll_IndexedAttrsToXML(edg_wll_Context ctx, char **message);
+int edg_wll_NotifResultToXML(edg_wll_Context ctx, time_t validity, char **message);
+void edg_wll_ErrorToXML(edg_wll_Context, char **);
+
+#endif
diff --git a/org.glite.lb.server/src/lbs_db.c b/org.glite.lb.server/src/lbs_db.c
new file mode 100644 (file)
index 0000000..53809d2
--- /dev/null
@@ -0,0 +1,219 @@
+#ident "$Header$"
+
+#include "mysql/mysql.h"       // MySql header file
+#include "mysql/mysqld_error.h"
+#include "mysql/errmsg.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <time.h>
+
+#include "lbs_db.h"
+#include "glite/lb/context-int.h"
+
+
+#define DEFAULTCS      "lbserver/@localhost:lbserver20"
+
+#define my_err() edg_wll_SetError(ctx,EDG_WLL_ERROR_DB_CALL,mysql_error((MYSQL *) ctx->mysql))
+
+struct _edg_wll_Stmt {
+       MYSQL_RES       *result;
+       edg_wll_Context ctx;
+};
+
+edg_wll_ErrorCode edg_wll_DBConnect(edg_wll_Context ctx,char *cs)
+{
+       char    *buf = NULL;
+       char    *host,*user,*pw,*db; 
+       char    *slash,*at,*colon;
+
+       if (!cs) cs = DEFAULTCS;
+
+       if (!(ctx->mysql = (void *) mysql_init(NULL))) 
+               return edg_wll_SetError(ctx,ENOMEM,NULL);
+
+       host = user = pw = db = NULL;
+
+       buf = strdup(cs);
+       slash = strchr(buf,'/');
+       at = strrchr(buf,'@');
+       colon = strrchr(buf,':');
+
+       if (!slash || !at || !colon) {
+               free(buf);
+               return edg_wll_SetError(ctx,EINVAL,"DB connect string");
+       }
+
+       *slash = *at = *colon = 0;
+       host = at+1;
+       user = buf;
+       pw = slash+1;
+       db = colon+1;
+
+       if (!mysql_real_connect((MYSQL *) ctx->mysql,host,user,pw,db,0,NULL,0)) {
+               free(buf);
+               return my_err();
+       }
+
+       free(buf);
+       return edg_wll_ResetError(ctx);
+}
+
+void edg_wll_DBClose(edg_wll_Context ctx)
+{
+       mysql_close((MYSQL *) ctx->mysql);
+       ctx->mysql = NULL;
+}
+
+int edg_wll_ExecStmt(edg_wll_Context ctx,char *txt,edg_wll_Stmt *stmt)
+{
+       int     err;
+       int     retry_nr = 0;
+       int     do_reconnect = 0;
+
+       edg_wll_ResetError(ctx);
+
+       if (stmt) {
+               *stmt = NULL;
+       }
+
+       while (retry_nr == 0 || do_reconnect) {
+               do_reconnect = 0;
+               if (mysql_query((MYSQL *) ctx->mysql,txt)) {
+                       /* error occured */
+                       switch (err = mysql_errno((MYSQL *) ctx->mysql)) {
+                               case 0:
+                                       break;
+                               case ER_DUP_ENTRY: 
+                                       edg_wll_SetError(ctx,EEXIST,mysql_error((MYSQL *) ctx->mysql));
+                                       return -1;
+                                       break;
+                               case CR_SERVER_LOST:
+                                       if (retry_nr <= 0) 
+                                               do_reconnect = 1;
+                                       break;
+                               default:
+                                       my_err();
+                                       return -1;
+                                       break;
+                       }
+               }
+               retry_nr++;
+       }
+
+       if (stmt) {
+               *stmt = malloc(sizeof(**stmt));
+               if (!*stmt) {
+                       edg_wll_SetError(ctx,ENOMEM,NULL);
+                       return -1;
+               }
+               memset(*stmt,0,sizeof(**stmt));
+               (**stmt).ctx = ctx;
+               (**stmt).result = mysql_store_result((MYSQL *) ctx->mysql);
+               if (!(**stmt).result) {
+                       if (mysql_errno((MYSQL *) ctx->mysql)) {
+                               my_err();
+                               return -1;
+                       }
+               }
+       } else {
+               MYSQL_RES       *r = mysql_store_result((MYSQL *) ctx->mysql);
+               mysql_free_result(r);
+       }
+       
+       return mysql_affected_rows((MYSQL *) ctx->mysql);
+}
+
+int edg_wll_FetchRow(edg_wll_Stmt stmt,char **res)
+{
+       MYSQL_ROW       row;
+       edg_wll_Context ctx = stmt->ctx;
+       int             nr,i;
+       unsigned long   *len;
+
+       edg_wll_ResetError(ctx);
+
+       if (!stmt->result) return 0;
+
+       if (!(row = mysql_fetch_row(stmt->result))) {
+               if (mysql_errno((MYSQL *) ctx->mysql)) {
+                       my_err();
+                       return -1;
+               } else return 0;
+       }
+
+       nr = mysql_num_fields(stmt->result);
+       len = mysql_fetch_lengths(stmt->result);
+       for (i=0; i<nr; i++) res[i] = len[i] ? strdup(row[i]) : strdup("");
+
+       return nr;
+}
+
+int edg_wll_QueryColumns(edg_wll_Stmt stmt,char **cols)
+{
+       int     i = 0;
+       MYSQL_FIELD     *f;
+
+       while ((f = mysql_fetch_field(stmt->result))) cols[i++] = f->name;
+       return i == 0;
+}
+
+void edg_wll_FreeStmt(edg_wll_Stmt *stmt)
+{
+       if (*stmt) {
+               if ((**stmt).result) mysql_free_result((**stmt).result);
+               free(*stmt);
+               *stmt = NULL;
+       }
+}
+
+
+char *edg_wll_TimeToDB(time_t t)
+{
+       struct tm       *tm = gmtime(&t);
+       char    tbuf[256];
+
+       sprintf(tbuf,"'%4d-%02d-%02d %02d:%02d:%02d'",tm->tm_year+1900,tm->tm_mon+1,
+               tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
+       
+       return strdup(tbuf);
+}
+
+time_t edg_wll_DBToTime(char *t)
+{
+       struct tm       tm;
+
+       memset(&tm,0,sizeof(tm));
+       setenv("TZ","UTC",1); tzset();
+       sscanf(t,"%4d-%02d-%02d %02d:%02d:%02d",
+               &tm.tm_year,&tm.tm_mon,&tm.tm_mday,
+               &tm.tm_hour,&tm.tm_min,&tm.tm_sec);
+       tm.tm_year -= 1900;
+       tm.tm_mon--;
+
+       return mktime(&tm);
+}
+
+int edg_wll_DBCheckVersion(edg_wll_Context ctx)
+{
+       MYSQL   *m = (MYSQL *) ctx->mysql;
+       const   char *ver_s = mysql_get_server_info(m);
+       int     major,minor,sub,version;
+
+       if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub))
+               return edg_wll_SetError(ctx,EINVAL,"retreiving MySQL version");
+
+       version = 10000*major + 100*minor + sub;
+
+       if (version < EDG_WLL_MYSQL_VERSION) {
+               char    msg[300];
+
+               snprintf(msg,sizeof msg,"Your MySQL version is %d. At least %d required.",version,EDG_WLL_MYSQL_VERSION);
+               return edg_wll_SetError(ctx,EINVAL,msg);
+       }
+
+       return edg_wll_ResetError(ctx);
+}
diff --git a/org.glite.lb.server/src/lbs_db.h b/org.glite.lb.server/src/lbs_db.h
new file mode 100644 (file)
index 0000000..3d7a344
--- /dev/null
@@ -0,0 +1,85 @@
+#ifndef _LBS_DB_H
+#define _LBS_DB_H
+
+#ident "$Header$"
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "glite/lb/consumer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define EDG_WLL_MYSQL_VERSION  40001
+
+typedef struct _edg_wll_Stmt *edg_wll_Stmt;
+
+edg_wll_ErrorCode edg_wll_DBConnect(
+       edg_wll_Context,        /* INOUT: */
+       char *          /* IN: connect string user/password@host:database */
+);
+
+void edg_wll_DBClose(edg_wll_Context);
+
+
+/* Parse and execute SQL statement. Returns number of rows selected, created 
+ * or affected by update, or -1 on error */
+
+int edg_wll_ExecStmt(
+       edg_wll_Context,        /* INOUT: */
+       char *,         /* IN: SQL statement */
+       edg_wll_Stmt *  /* OUT: statement handle. Usable for select only */
+);
+
+
+/* Fetch next row of select statement. 
+ * All columns are returned as fresh allocated strings 
+ *
+ * return values:
+ *     >0 - number of fields of the retrieved row
+ *      0 - no more rows
+ *     -1 - error
+ *
+ * Errors are stored in context passed to previous edg_wll_ExecStmt() */
+
+int edg_wll_FetchRow(
+       edg_wll_Stmt,   /* IN: statement */
+       char **         /* OUT: array of fetched values. 
+                        *      As number of columns is fixed and known,
+                        *      expects allocated array of pointers here */
+);
+
+/* Retrieve column names of a query statement */
+
+int edg_wll_QueryColumns(
+       edg_wll_Stmt,   /* IN: statement */
+       char **         /* OUT: result set column names. Expects allocated array. */
+);
+
+/* Free the statement structure */
+
+void edg_wll_FreeStmt(
+       edg_wll_Stmt *    /* INOUT: statement */
+);
+
+
+/* convert time_t into database-specific time string 
+ * returns pointer to static area that is changed by subsequent calls */
+char *edg_wll_TimeToDB(time_t);
+time_t edg_wll_DBToTime(char *);
+
+extern edg_wll_ErrorCode edg_wll_Open(edg_wll_Context ctx, char *cs);
+
+/**
+ * Check database version.
+ */
+int edg_wll_DBCheckVersion(edg_wll_Context);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/org.glite.lb.server/src/load.c b/org.glite.lb.server/src/load.c
new file mode 100644 (file)
index 0000000..0f08964
--- /dev/null
@@ -0,0 +1,210 @@
+#ident "$Header$"
+
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+#include "glite/lb/trio.h"
+
+#include "glite/lb/context-int.h"
+#include "glite/lb/events_parse.h"
+#include "glite/lb/ulm_parse.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/events.h"
+#include "glite/lb/dump.h"
+#include "glite/lb/load.h"
+
+#include "store.h"
+#include "purge.h"
+#include "lbs_db.h"
+#include "query.h"
+#include "get_events.h"
+#include "server_state.h"
+
+
+static int read_line(char **buff, int fd);
+
+int edg_wll_LoadEvents(edg_wll_Context ctx,const edg_wll_LoadRequest *req,edg_wll_LoadResult *result)
+{
+       int                                     fd,
+                                               reject_fd = -1,
+                                               readret, i;
+       char                       *line = NULL,
+                                               buff[30];
+       edg_wll_Event      *event;
+       edg_wlc_JobId           jobid = NULL;
+
+
+       edg_wll_ResetError(ctx);
+       
+       if ( !req->server_file )
+               return edg_wll_SetError(ctx, EINVAL, "Server file is not specified for load");
+
+       if ( (fd = open(req->server_file, O_RDONLY)) == -1 )
+               return edg_wll_SetError(ctx, errno, "Server can not open the file");
+
+       memset(result,0,sizeof(*result));
+       i = 0;
+       while ( 1 )
+       {
+               /*      Read one line
+                */
+               if ( (readret = read_line(&line, fd)) == -1 )
+                       return edg_wll_SetError(ctx, errno, "reading dump file");
+
+               if ( readret == 0 )
+                       break;
+
+               i++;
+               if (   sscanf(line, "DG.ARRIVED=%s %*s", buff) != 1
+                       || edg_wll_ParseEvent(ctx, line, &event) )
+               {
+                       char errs[100];
+                       sprintf(errs, "Error parsing event at line %d", i);
+                       if ( !edg_wll_Error(ctx,NULL,NULL) )
+                               edg_wll_SetError(ctx, EINVAL, errs);
+                       fprintf(stderr, errs);
+                       continue;
+               }
+               edg_wll_ULMDateToTimeval(buff, &(event->any.arrived));
+
+               if ( i == 1 )
+               {
+                       result->from = event->any.arrived.tv_sec;
+                       result->to = event->any.arrived.tv_sec;
+               }
+               ctx->event_load = 1;
+               if ( edg_wll_StoreEvent(ctx, event, NULL) )
+               {
+                       int             len = strlen(line),
+                                       total = 0,
+                                       written;
+
+                       fprintf(stderr, "Can't store event\n");
+                       if ( reject_fd == -1 )
+                       {
+                               char   *s, *s1;
+
+                               if ( result->server_file != NULL )
+                                       goto cycle_clean;
+
+                               s1 = strdup(req->server_file);
+                               s = strrchr(s1, '/');
+                               if ( s )
+                               {
+                                       *s = '\0';
+                                       reject_fd = edg_wll_CreateFileStorage(ctx,FILE_TYPE_LOAD,s1,&(result->server_file));
+                               }
+                               else
+                                       reject_fd = edg_wll_CreateFileStorage(ctx,FILE_TYPE_LOAD,NULL,&(result->server_file));
+                               if ( reject_fd == -1 )
+                                       goto cycle_clean;
+                               printf("New reject file %s created\n", result->server_file);
+                               free(s1);
+                       }
+                       /*      Save the line into "reject_file"
+                        */
+                       while (total != len) {
+                               written = write(reject_fd, line+total, len-total);
+                               if (written < 0 && errno != EAGAIN) {
+                                       edg_wll_SetError(ctx, errno, "writing load reject file");
+                                       free(line);
+                                       break;
+                               }
+                               total += written;
+                       }
+                       write(reject_fd,"\n",1);
+               }
+               else
+               {
+                       result->to = event->any.arrived.tv_sec;
+                       if ( jobid )
+                       {
+                               char *md5_jobid = edg_wlc_JobIdGetUnique(jobid);
+                               
+                               if ( strcmp(md5_jobid, edg_wlc_JobIdGetUnique(event->any.jobId)) )
+                               {
+                                       edg_wll_JobStat st;
+
+                                       edg_wll_JobStatus(ctx, jobid, 0, &st);
+                                       edg_wll_FreeStatus(&st);
+
+                                       edg_wlc_JobIdFree(jobid);
+                                       edg_wlc_JobIdDup(event->any.jobId, &jobid);
+                               }
+                               free(md5_jobid);
+                       }
+                       else
+                               edg_wlc_JobIdDup(event->any.jobId, &jobid);
+               }
+
+
+cycle_clean:
+               ctx->event_load = 0;
+               edg_wll_FreeEvent(event);
+       }
+
+       if ( jobid )
+       {
+               edg_wll_JobStat st;
+
+               edg_wll_JobStatus(ctx, jobid, 0, &st);
+               edg_wll_FreeStatus(&st);
+               edg_wlc_JobIdFree(jobid);
+       }
+
+       if ( reject_fd != -1 )
+               close(reject_fd);
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+#define BUFFSZ                 1024
+
+static int read_line(char **buff, int fd)
+{
+       int             ct, i;
+
+
+       if ( *buff == NULL )
+       {
+               *buff = malloc(BUFFSZ); 
+               if ( *buff == NULL )
+                       return -1;
+       }
+
+       i = 0;
+       while ( 1 )
+       {
+               if ( (ct = read(fd, (*buff)+i, 1)) == -1 )
+                       return -1;
+
+               if ( ct == 0 )
+                       return 0;
+
+               if ( (*buff)[i] == '\n' )
+               {
+                       (*buff)[i--] = '\0';
+                       while ( (i != -1) && isspace((*buff)[i]) ) i--;
+                       if ( i == -1 )
+                       {
+                               /**     empty line
+                                */
+                               i = 0;
+                               continue;
+                       }
+                       else
+                               return 1;
+               }
+
+               i++;
+       }
+}
diff --git a/org.glite.lb.server/src/lock.c b/org.glite.lb.server/src/lock.c
new file mode 100644 (file)
index 0000000..a11e27c
--- /dev/null
@@ -0,0 +1,32 @@
+#include <unistd.h>
+#include <sys/ipc.h>
+#include <sys/sem.h>
+#include <errno.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/lb/context-int.h"
+#include "lock.h"
+
+
+int edg_wll_LockUnlockJob(const edg_wll_Context ctx,const edg_wlc_JobId job,int lock)
+{
+       struct sembuf   s;
+       char    *un = edg_wlc_JobIdGetUnique(job);
+       int     n,i;
+       static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
+       
+       if (!un) return edg_wll_SetError(ctx,EINVAL,"jobid");
+
+       for (n=0; n<sizeof b64 && b64[n] != un[0]; n++);
+       for (i=0; i<sizeof b64 && b64[i] != un[1]; i++);
+       n += i<<6;
+
+       fprintf(stderr,"[%d] semop(%d,%d) \n",getpid(),n % ctx->semaphores,lock);
+
+       s.sem_num = n % ctx->semaphores;
+       s.sem_op = lock;
+       s.sem_flg = SEM_UNDO;
+
+       if (semop(ctx->semset,&s,1)) return edg_wll_SetError(ctx,errno,"edg_wll_LockUnlockJob()");
+       return edg_wll_ResetError(ctx);
+}
diff --git a/org.glite.lb.server/src/lock.h b/org.glite.lb.server/src/lock.h
new file mode 100644 (file)
index 0000000..c67ec6d
--- /dev/null
@@ -0,0 +1,4 @@
+#define edg_wll_LockJob(ctx,job) edg_wll_LockUnlockJob((ctx),(job),-1)
+#define edg_wll_UnlockJob(ctx,job) edg_wll_LockUnlockJob((ctx),(job),1)
+
+int edg_wll_LockUnlockJob(const edg_wll_Context,const edg_wlc_JobId,int);
diff --git a/org.glite.lb.server/src/notif_match.c b/org.glite.lb.server/src/notif_match.c
new file mode 100644 (file)
index 0000000..d50de60
--- /dev/null
@@ -0,0 +1,166 @@
+#ident "$Header$"
+
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "glite/lb/producer.h"
+#include "glite/lb/consumer.h"
+#include "glite/lb/context.h"
+#include "glite/lb/trio.h"
+
+#include "lbs_db.h"
+#include "lb_authz.h"
+#include "lb_xml_parse.h"
+#include "query.h"
+#include "il_notification.h"
+
+
+
+static int notif_match_conditions(edg_wll_Context,const edg_wll_JobStat *,const char *);
+static int notif_check_acl(edg_wll_Context,const edg_wll_JobStat *,const char *);
+
+int edg_wll_NotifExpired(edg_wll_Context,const char *);
+
+int edg_wll_NotifMatch(edg_wll_Context ctx, const edg_wll_JobStat *stat)
+{
+       edg_wll_NotifId         nid = NULL;
+       char    *jobq,*ju = NULL,*jobc[5];
+       edg_wll_Stmt    jobs = NULL;
+       int     ret,i;
+       time_t  now = time(NULL);
+
+       edg_wll_ResetError(ctx);
+
+       if ( (ret = edg_wll_NotifIdCreate(ctx->srvName, ctx->srvPort, &nid)) )
+       {
+               edg_wll_SetError(ctx, ret, "edg_wll_NotifMatch()");
+               goto err;
+       }
+       trio_asprintf(&jobq,
+               "select distinct n.notifid,n.destination,n.valid,u.cert_subj,n.conditions "
+               "from notif_jobs j,users u,notif_registrations n "
+               "where j.notifid=n.notifid and n.userid=u.userid "
+               "   and j.jobid = '%|Ss'",ju = edg_wlc_JobIdGetUnique(stat->jobId));
+
+       free(ju);
+
+       if (edg_wll_ExecStmt(ctx,jobq,&jobs) < 0) goto err;
+
+       while ((ret = edg_wll_FetchRow(jobs,jobc)) > 0) {
+               if (now > edg_wll_DBToTime(jobc[2]))
+                       edg_wll_NotifExpired(ctx,jobc[0]);
+               else if (notif_match_conditions(ctx,stat,jobc[4]) &&
+                               notif_check_acl(ctx,stat,jobc[3]))
+               {
+                       char                       *dest, *aux;
+                       int                                     port;
+
+                       fprintf(stderr,"NOTIFY: %s, job %s\n",jobc[0],
+                                       ju = edg_wlc_JobIdGetUnique(stat->jobId));
+                       free(ju);
+
+                       dest = strdup(jobc[1]);
+                       if ( !(aux = strchr(dest, ':')) )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "Can't parse notification destination");
+                               free(dest);
+                               for (i=0; i<sizeof(jobc)/sizeof(jobc[0]); i++) free(jobc[i]);
+                               goto err;
+                       }
+                       *aux = 0;
+                       aux++;
+                       port = atoi(aux);
+                       
+                       if (   edg_wll_NotifIdSetUnique(&nid, jobc[0]) )
+                       {
+                               free(dest);
+                               goto err;
+                       }
+                       /* XXX: only temporary hack!!!
+                        */
+                       ctx->p_instance = strdup("");
+                       if ( edg_wll_NotifJobStatus(ctx, nid, dest, port, jobc[3], *stat) )
+                       {
+                               free(dest);
+                               for (i=0; i<sizeof(jobc)/sizeof(jobc[0]); i++) free(jobc[i]);
+                               goto err;
+                       }
+                       free(dest);
+               }
+               
+               for (i=0; i<sizeof(jobc)/sizeof(jobc[0]); i++) free(jobc[i]);
+       }
+       if (ret < 0) goto err;
+       
+err:
+       if ( nid ) edg_wll_NotifIdFree(nid);
+       free(jobq);
+       edg_wll_FreeStmt(&jobs);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_NotifExpired(edg_wll_Context ctx,const char *notif)
+{
+       /* TODO */
+       return 0;
+}
+
+
+static int notif_match_conditions(edg_wll_Context ctx,const edg_wll_JobStat *stat,const char *cond)
+{
+       edg_wll_QueryRec        **c,**p;
+       int                     match,i;
+
+       if (!cond) return 1;
+
+       if (parseJobQueryRec(ctx,cond,strlen(cond),&c)) {
+               fputs("notif_match_conditions(): parseJobQueryRec failed\n",stderr);
+               syslog(LOG_ERR,"notif_match_conditions(): parseJobQueryRec failed");
+               return 1;
+       }
+
+       match = match_status(ctx,stat,(const edg_wll_QueryRec **) c);
+       if ( c )
+       {
+               for (p = c; *p; p++) {
+                       for (i=0; (*p)[i].attr; i++)
+                               edg_wll_QueryRecFree((*p)+i);
+                       free(*p);
+               }
+               free(c);
+       }
+       return match;
+}
+
+/* FIXME: does not favour any VOMS information in ACL
+ * effective VOMS groups of the recipient are not available here, should be 
+ * probably stored along with the registration.
+ */
+static int notif_check_acl(edg_wll_Context ctx,const edg_wll_JobStat *stat,const char *recip)
+{
+       edg_wll_Acl     acl = calloc(1,sizeof *acl);
+       GACLacl         *gacl;
+       int             ret;
+
+       edg_wll_ResetError(ctx);
+       if (ctx->noAuth || strcmp(stat->owner,recip) == 0) return 1;
+
+       ret = edg_wll_DecodeACL(stat->acl,&gacl);
+       if (ret) {
+               edg_wll_SetError(ctx,EINVAL,"decoding ACL");
+               return 0;
+       }
+
+       acl->string = stat->acl; 
+       acl->value = gacl;
+
+       ret = edg_wll_CheckACL(ctx, acl, EDG_WLL_PERM_READ);
+
+       acl->string = NULL;
+       edg_wll_FreeAcl(acl);
+
+       return !ret;
+}
diff --git a/org.glite.lb.server/src/notification.c b/org.glite.lb.server/src/notification.c
new file mode 100644 (file)
index 0000000..2ddbdaa
--- /dev/null
@@ -0,0 +1,436 @@
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "glite/wms/jobid/strmd5.h"
+#include "glite/lb/trio.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/xml_parse.h"
+#include "glite/lb/notification.h"
+#include "lbs_db.h"
+
+
+static char       *get_user(edg_wll_Context ctx, int create);
+static int             update_notif(edg_wll_Context, const edg_wll_NotifId,
+                                                       const char *, const char *, const char *);
+
+int edg_wll_NotifNewServer(
+       edg_wll_Context                                 ctx,
+       edg_wll_QueryRec const * const *conditions,
+       char const                                         *address_override,
+       const edg_wll_NotifId                   nid,
+       time_t                                             *valid)
+{
+       int                                     i, j,
+                                               ct,
+                                               new_rows;
+       char                       *q                   = NULL,
+                                          *nid_s               = NULL,
+                                          *time_s              = NULL,
+                                          *addr_s              = NULL,
+                                          *xml_conds   = NULL,
+                                          *owner               = NULL,
+                                         **jobs                = NULL;
+       edg_wll_QueryRec  **nconds              = NULL;
+
+
+       /*      Format notification ID
+        */
+       if ( !(nid_s = edg_wll_NotifIdGetUnique(nid)) )
+               goto cleanup;
+
+       /*      Get notification owner
+        */
+       if ( !(owner = get_user(ctx, 1)) )
+               goto cleanup;
+
+       /*      Format conditions
+        *      - first of all separate all jobids
+        *      - then format new condition list without jobids and encode it into an XML string
+        */
+       if ( !conditions || !conditions[0] )
+       {
+               edg_wll_SetError(ctx, EINVAL, "Empty condition list");
+               goto cleanup;
+       }
+       for ( new_rows = ct = i = 0; conditions[i]; i++ )
+       {
+               if ( conditions[i][0].attr && conditions[i][0].attr != EDG_WLL_QUERY_ATTR_JOBID )
+                       new_rows++;
+               for ( j = 0; conditions[i][j].attr; j++ )
+                       if ( conditions[i][j].attr == EDG_WLL_QUERY_ATTR_JOBID )
+                               ct++;
+       }
+       if ( !ct )
+       {
+               edg_wll_SetError(ctx, EINVAL, "Notification has to bind to at least one jobID");
+               goto cleanup;
+       }
+       if (   !(jobs = calloc(ct+1, sizeof(char *)))
+               || !(nconds = calloc(new_rows+1, sizeof(edg_wll_QueryRec *))) )
+       {
+               edg_wll_SetError(ctx, errno, NULL);
+               goto cleanup;
+       }
+       for ( ct = i = 0; conditions[i]; i++ )
+               for ( j = 0; conditions[i][j].attr; j++ )
+                       if ( conditions[i][j].attr == EDG_WLL_QUERY_ATTR_JOBID )
+                               if ( !(jobs[ct++] = edg_wlc_JobIdGetUnique(conditions[i][j].value.j)) )
+                               {
+                                       edg_wll_SetError(ctx, errno, NULL);
+                                       goto cleanup;
+                               }
+       for ( new_rows = i = 0; conditions[i]; i++ )
+               if ( conditions[i][0].attr && conditions[i][0].attr != EDG_WLL_QUERY_ATTR_JOBID )
+                       /*      !!! DO NOT DEALLOCATE this arrays (it is not neccessary to allocate new
+                        *      mem - it's used only once and only for xml parsing
+                        */
+                       nconds[new_rows++] = (edg_wll_QueryRec  *) (conditions[i]);
+       if ( edg_wll_JobQueryRecToXML(ctx, (edg_wll_QueryRec const * const *) nconds, &xml_conds) )
+       {
+               /*      XXX: edg_wll_JobQueryRecToXML() do not set errors in context!
+                *                      can't get propper error number :(
+                */
+               edg_wll_SetError(ctx, errno, "Can't encode data into xml");
+               goto cleanup;
+       }
+
+       /*      Format time of validity
+        */
+       *valid = time(NULL);
+       if (   ctx->peerProxyValidity
+               && (ctx->peerProxyValidity - *valid) < ctx->notifDuration )
+               *valid = ctx->peerProxyValidity;
+       else
+               *valid += ctx->notifDuration;
+       
+       if ( !(time_s = strdup(edg_wll_TimeToDB(*valid))) )
+       {
+               edg_wll_SetError(ctx, errno, NULL);
+               goto cleanup;
+       }
+       time_s[strlen(time_s)-1] = 0;
+
+       /*      Format the address
+        */
+       if ( address_override )
+       {
+               char   *aux;
+
+               if ( !(aux = strchr(address_override, ':')) )
+               {
+                       edg_wll_SetError(ctx, EINVAL, "Addres overrirde not in format host:port");
+                       goto cleanup;
+               }
+               if ( !strncmp(address_override, "0.0.0.0", aux-address_override) )
+                       trio_asprintf(&addr_s, "%s:%s", ctx->connPool[ctx->connToUse].peerName, aux+1);
+       }
+
+       /*      Format DB insert statement
+        */
+       trio_asprintf(&q,
+                               "insert into notif_registrations(notifid,destination,valid,userid,conditions) "
+                               "values ('%|Ss','%|Ss','%|Ss','%|Ss', '<and>%|Ss</and>')",
+                               nid_s, addr_s? addr_s: address_override, time_s+1, owner, xml_conds);
+
+       if ( edg_wll_ExecStmt(ctx, q, NULL) < 0 )
+               goto cleanup;
+
+       for ( i = 0; jobs[i]; i++ )
+       {
+               free(q);
+               trio_asprintf(&q,
+                               "insert into notif_jobs(notifid,jobid) values ('%|Ss','%|Ss')",
+                               nid_s, jobs[i]);
+               if ( edg_wll_ExecStmt(ctx, q, NULL) < 0 )
+               {
+                       /*      XXX: Remove uncoplete registration?
+                        *               Which error has to be returned?
+                        */
+                       free(q);
+                       trio_asprintf(&q, "delete from notif_jobs where notifid='%|Ss'", nid_s);
+                       edg_wll_ExecStmt(ctx, q, NULL);
+                       free(q);
+                       trio_asprintf(&q, "delete from notif_registrations where notifid='%|Ss'", nid_s);
+                       edg_wll_ExecStmt(ctx, q, NULL);
+                       goto cleanup;
+               }
+       }
+
+
+cleanup:
+       if ( q ) free(q);
+       if ( nid_s ) free(nid_s);
+       if ( time_s ) free(time_s);
+       if ( addr_s ) free(addr_s);
+       if ( xml_conds ) free(xml_conds);
+       if ( owner ) free(owner);
+       if ( jobs )
+       {
+               for ( i = 0; jobs[i]; i++ )
+                       free(jobs[i]);
+               free(jobs);
+       }
+       if ( nconds ) free(nconds);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+
+int edg_wll_NotifBindServer(
+       edg_wll_Context                                 ctx,
+       const edg_wll_NotifId                   nid,
+       const char                                         *address_override,
+       time_t                                             *valid)
+{
+       char       *time_s = NULL;
+
+
+       if ( !address_override )
+       {
+               edg_wll_SetError(ctx, EINVAL, "Address parameter not given");
+               goto cleanup;
+       }
+
+       /*      Format time of validity
+        */
+       *valid = time(NULL);
+       if (   ctx->peerProxyValidity
+               && (ctx->peerProxyValidity - *valid) < ctx->notifDuration )
+               *valid = ctx->peerProxyValidity;
+       else
+               *valid += ctx->notifDuration;
+
+       if ( !(time_s = strdup(edg_wll_TimeToDB(*valid))) )
+       {
+               edg_wll_SetError(ctx, errno, "Formating validity time");
+               goto cleanup;
+       }
+       time_s[strlen(time_s)-1] = 0;
+
+       update_notif(ctx, nid, NULL, address_override, (const char *)(time_s+1));
+
+cleanup:
+       if ( time_s ) free(time_s);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+int edg_wll_NotifChangeServer(
+       edg_wll_Context                                 ctx,
+       const edg_wll_NotifId                   id,
+       edg_wll_QueryRec const * const *conditions,
+       edg_wll_NotifChangeOp                   op)
+{
+       return edg_wll_SetError(ctx, EINVAL, "Not yet implemented");
+}
+
+int edg_wll_NotifRefreshServer(
+       edg_wll_Context                                 ctx,
+       const edg_wll_NotifId                   nid,
+       time_t                                             *valid)
+{
+       char       *time_s = NULL;
+
+
+       /*      Format time of validity
+        */
+       *valid = time(NULL);
+       if (   ctx->peerProxyValidity
+               && (ctx->peerProxyValidity - *valid) < ctx->notifDuration )
+               *valid = ctx->peerProxyValidity;
+       else
+               *valid += ctx->notifDuration;
+
+       if ( !(time_s = strdup(edg_wll_TimeToDB(*valid))) )
+       {
+               edg_wll_SetError(ctx, errno, "Formating validity time");
+               goto cleanup;
+       }
+       time_s[strlen(time_s)-1] = 0;
+
+       update_notif(ctx, nid, NULL, NULL, time_s+1);
+
+cleanup:
+       if ( time_s ) free(time_s);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+int edg_wll_NotifDropServer(
+       edg_wll_Context                                 ctx,
+       edg_wll_NotifId                            *nid)
+{
+       char       *owner = NULL,
+                          *nid_s = NULL,
+                          *stmt;
+       int                     ret;
+
+
+       if ( !(owner = get_user(ctx, 0)) )
+       {
+               if ( !edg_wll_Error(ctx, NULL, NULL) )
+                       edg_wll_SetError(ctx, EPERM, "Unknown user");
+
+               return edg_wll_Error(ctx, NULL, NULL);
+       }
+
+       if ( !(nid_s = edg_wll_NotifIdGetUnique(nid)) )
+               goto cleanup;
+
+       /* Only the owner could remove the notification registration
+        */
+       trio_asprintf(&stmt,
+                               "delete from notif_registrations where notifid='%|Ss' and userid='%|Ss'",
+                               nid_s, owner);
+       if ( (ret = edg_wll_ExecStmt(ctx, stmt, NULL)) < 0 )
+               goto cleanup;
+       free(stmt);
+       if ( ret == 0 )
+       {
+               trio_asprintf(&stmt,
+                               "select notifid from notif_registrations where notifid='%|Ss'", nid_s);
+               ret = edg_wll_ExecStmt(ctx, stmt, NULL);
+               if ( ret == 0 )
+                       edg_wll_SetError(ctx, ENOENT, "Unknown notification ID");
+               else if ( ret > 0 )
+                       edg_wll_SetError(ctx, EPERM, NULL);
+
+               goto cleanup;
+       }
+       trio_asprintf(&stmt, "delete from notif_jobs where notifid='%|Ss'", nid_s);
+       edg_wll_ExecStmt(ctx, stmt, NULL);
+
+cleanup:
+       if ( owner ) free(owner);
+       if ( nid_s ) free(nid_s);
+       if ( stmt ) free(stmt);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
+
+static char *get_user(edg_wll_Context ctx, int create)
+{
+       edg_wll_Stmt    stmt    = NULL;
+       char               *userid      = NULL,
+                                  *q           = NULL;
+       int                             ret;
+
+
+       if ( !ctx->peerName )
+       {
+               edg_wll_SetError(ctx, EPERM, "Annonymous notifications not allowed");
+               goto cleanup;
+       }
+       trio_asprintf(&q, "select userid from users where cert_subj='%|Ss'", ctx->peerName);
+       if ( edg_wll_ExecStmt(ctx, q, &stmt) < 0 )
+               goto cleanup;
+
+       /*      returned value:
+        *      0               no user find - continue only when 'create' parameter is set
+        *      >0              user found - return selected value
+        *      <0              SQL error
+        */
+       if ( ((ret = edg_wll_FetchRow(stmt, &userid)) != 0) || !create )
+               goto cleanup;
+
+       if ( !(userid = strdup(strmd5(ctx->peerName, NULL))) )
+       {
+               edg_wll_SetError(ctx, errno, "Creating user ID");
+               goto cleanup;
+       }
+       free(q);
+       trio_asprintf(&q, "insert into users(userid,cert_subj) values ('%|Ss','%|Ss')",
+                       userid, ctx->peerName);
+       if ( edg_wll_ExecStmt(ctx, q, NULL) < 0 )
+       {
+               if ( edg_wll_Error(ctx,NULL,NULL) != EEXIST )
+               {
+                       free(userid);
+                       userid = NULL;
+               }
+               else
+                       edg_wll_ResetError(ctx);
+       }
+
+cleanup:
+       if ( q ) free(q);
+       if ( stmt ) edg_wll_FreeStmt(&stmt);
+
+       return userid;
+}
+
+static int update_notif(
+       edg_wll_Context                                 ctx,
+       const edg_wll_NotifId                   nid,
+       const char                                         *conds,
+       const char                                         *dest,
+       const char                                         *valid)
+{
+       char       *owner       = NULL,
+                          *nid_s       = NULL,
+                          *stmt, *aux;
+       int                     ret;
+
+
+       if ( !(owner = get_user(ctx, 0)) )
+       {
+               if ( !edg_wll_Error(ctx, NULL, NULL) )
+                       edg_wll_SetError(ctx, EPERM, "Unknown user");
+
+               return edg_wll_Error(ctx, NULL, NULL);
+       }
+
+       if ( !(nid_s = edg_wll_NotifIdGetUnique(nid)) )
+               goto cleanup;
+
+       /*      Format SQL update string
+        *      (Only the owner could update the notification registration)
+        */
+       stmt = strdup("update notif_registrations set");
+       if ( dest )
+       {
+               trio_asprintf(&aux, "%s destination='%|Ss'", stmt, dest);
+               free(stmt);
+               stmt = aux;
+       }
+       if ( valid )
+       {
+               trio_asprintf(&aux, "%s %svalid='%|Ss'", stmt, dest? ",": "", valid);
+               free(stmt);
+               stmt = aux;
+       }
+       if ( conds )
+       {
+               trio_asprintf(&aux, "%s %sconditions='%|Ss'",
+                                               stmt, (dest||valid)? ",": "", conds);
+               free(stmt);
+               stmt = aux;
+       }
+       trio_asprintf(&aux, "%s where notifid='%|Ss' and userid='%|Ss'",
+                                               stmt, nid_s, owner);
+       free(stmt);
+       stmt = aux;
+
+       if ( (ret = edg_wll_ExecStmt(ctx, stmt, NULL)) < 0 )
+               goto cleanup;
+       if ( ret == 0 )
+       {
+               free(stmt);
+               trio_asprintf(&stmt,
+                               "select notifid from notif_registrations where notifid='%|Ss'", nid_s);
+               ret = edg_wll_ExecStmt(ctx, stmt, NULL);
+               if ( ret == 0 )
+                       edg_wll_SetError(ctx, ENOENT, "Unknown notification ID");
+               else if ( ret > 0 )
+                       edg_wll_SetError(ctx, EPERM, "Updating notification records");
+       }
+
+cleanup:
+       if ( owner ) free(owner);
+       if ( nid_s ) free(nid_s);
+       if ( stmt ) free(stmt);
+
+       return edg_wll_Error(ctx, NULL, NULL);
+}
diff --git a/org.glite.lb.server/src/openserver.c b/org.glite.lb.server/src/openserver.c
new file mode 100644 (file)
index 0000000..5b34974
--- /dev/null
@@ -0,0 +1,18 @@
+#ident "$Header$"
+
+#include <stdlib.h>
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/context.h"
+
+#include "lbs_db.h"
+
+edg_wll_ErrorCode edg_wll_Open(edg_wll_Context ctx, char *cs)
+{
+       return edg_wll_DBConnect(ctx,cs) ? edg_wll_Error(ctx,NULL,NULL) : 0;
+}
+
+edg_wll_ErrorCode edg_wll_Close(edg_wll_Context ctx)
+{
+       return edg_wll_ResetError(ctx);
+}
diff --git a/org.glite.lb.server/src/purge.h b/org.glite.lb.server/src/purge.h
new file mode 100644 (file)
index 0000000..8841e12
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef __EDG_WORKLOAD_LOGGING_COMMON_PURGE_H__
+#define __EDG_WORKLOAD_LOGGING_COMMON_PURGE_H__
+
+/** Server side implementation
+ *  besides output to the SSL stream (in the context) it may produce
+ *  the server-side dump files
+ */
+int edg_wll_PurgeServer(
+       edg_wll_Context ctx,
+       const edg_wll_PurgeRequest *request
+);
+
+#define                FILE_TYPE_ANY           ""
+#define                FILE_TYPE_PURGE         "purge"
+#define                FILE_TYPE_DUMP          "dump"
+#define                FILE_TYPE_LOAD          "load"
+
+extern int edg_wll_CreateTmpFileStorage(
+       edg_wll_Context ctx,
+       char *prefix,
+       char **fname
+);
+
+extern int edg_wll_CreateFileStorageFromTmp(
+       edg_wll_Context ctx,
+       char *tmp_type,
+       char *file_type,
+       char **fname
+);
+
+extern int edg_wll_CreateFileStorage(
+       edg_wll_Context ctx,
+       char *file_type,
+       char *prefix,
+       char **fname
+);
+
+#define edg_wll_CreateTmpDumpFile(ctx, f)      edg_wll_CreateTmpFileStorage(ctx,ctx->dumpStorage,f)
+#define edg_wll_CreateTmpPurgeFile(ctx, f)     edg_wll_CreateTmpFileStorage(ctx,ctx->purgeStorage,f)
+
+#define edg_wll_CreateDumpFileFromTmp(ctx, f, f2)      \
+                       edg_wll_CreateFileStorageFromTmp(ctx, f, FILE_TYPE_DUMP, f2)
+#define edg_wll_CreatePurgeFileFromTmp(ctx, f, f2)     \
+                       edg_wll_CreateFileStorageFromTmp(ctx, f, FILE_TYPE_PURGE, f2)
+
+#define edg_wll_CreateDumpFile(ctx, f)         edg_wll_CreateFileStorage(ctx,FILE_TYPE_DUMP,NULL,f)
+#define edg_wll_CreatePurgeFile(ctx, f)                edg_wll_CreateFileStorage(ctx,FILE_TYPE_PURGE,NULL,f)
+
+#endif
diff --git a/org.glite.lb.server/src/query.c b/org.glite.lb.server/src/query.c
new file mode 100644 (file)
index 0000000..a4da45a
--- /dev/null
@@ -0,0 +1,1414 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+
+#include "glite/wms/jobid/strmd5.h"
+
+#include "glite/lb/consumer.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+
+
+#include "get_events.h"
+#include "index.h"
+#include "query.h"
+#include "store.h"
+#include "lb_authz.h"
+
+#define FL_SEL_STATUS          1
+#define FL_SEL_TAGS                    (1<<1)
+#define FL_SEL_JOB                     (1<<2)
+
+
+static int check_event_query_index(edg_wll_Context,const edg_wll_QueryRec **,const edg_wll_QueryRec **);
+static int check_job_query_index(edg_wll_Context, const edg_wll_QueryRec **);
+static char *jc_to_head_where(edg_wll_Context, const edg_wll_QueryRec **, int *);
+static char *ec_to_head_where(edg_wll_Context, const edg_wll_QueryRec **);
+static int match_flesh_conditions(const edg_wll_Event *,const edg_wll_QueryRec **);
+static int check_strict_jobid_cond(edg_wll_Context, const edg_wll_QueryRec **);
+
+static int cmp_string(const char *,edg_wll_QueryOp,const char *);
+static int is_all_query(const edg_wll_QueryRec **);
+
+
+
+#define sizofa(a) (sizeof(a)/sizeof((a)[0]))
+
+int edg_wll_QueryEventsServer(
+        edg_wll_Context ctx,
+               int     noAuth,
+        const edg_wll_QueryRec **job_conditions,
+        const edg_wll_QueryRec **event_conditions,
+        edg_wll_Event **events)
+{
+       char               *job_where = NULL,
+                                  *event_where = NULL,
+                                  *qbase = NULL,
+                                  *q = NULL,
+                                  *res[11];
+       edg_wll_Event  *out = NULL;
+       edg_wll_Stmt    sh = NULL;
+       int                             i = 0,
+                                       ret = 0,
+                                       offset = 0, limit = 0,
+                                       limit_loop = 1,
+                                       eperm = 0;
+
+
+       edg_wll_ResetError(ctx);
+
+       if ( (ctx->p_query_results == EDG_WLL_QUERYRES_ALL) &&
+                       (!job_conditions || !job_conditions[0] || job_conditions[1] ||
+                       (job_conditions[0][0].attr != EDG_WLL_QUERY_ATTR_JOBID) ||
+                       (job_conditions[0][1].attr != EDG_WLL_QUERY_ATTR_UNDEF)) )
+       {
+               edg_wll_SetError(ctx, EINVAL, "Invalid parameter EDG_WLL_PARAM_QUERY_RESULTS");
+               goto cleanup;
+       }
+
+       if ((!ctx->noIndex && check_event_query_index(ctx,job_conditions,event_conditions)) ||
+               check_strict_jobid_cond(ctx,job_conditions) ||
+               check_strict_jobid_cond(ctx,event_conditions))
+               goto cleanup;
+
+       if (event_conditions && *event_conditions && (*event_conditions)->attr &&
+               !(event_where = ec_to_head_where(ctx,event_conditions)))
+               goto cleanup;
+
+       if ( job_conditions && *job_conditions && (*job_conditions)->attr &&
+               !(job_where = jc_to_head_where(ctx, job_conditions, &i)) )
+               goto cleanup;
+
+/* XXX: similar query in srv_purge.c ! They has to match due to common
+ * convert_event_head() called on the result
+ */
+       trio_asprintf(&qbase,"SELECT e.event,j.userid,j.dg_jobid,e.code,"
+               "e.prog,e.host,u.cert_subj,e.time_stamp,e.usec,e.level,e.arrived "
+               "FROM events e,users u,jobs j%s "
+               "WHERE %se.jobid=j.jobid AND e.userid=u.userid AND e.code != %d "
+               "%s %s %s %s",
+               i & FL_SEL_STATUS                       ? ",states s"                           : "",
+               i & FL_SEL_STATUS                       ? "s.jobid=j.jobid AND "        : "",
+               EDG_WLL_EVENT_UNDEF,
+               job_where                                       ? "AND"                                         : "",
+               job_where                                       ? job_where                                     : "",
+               event_where                                     ? "AND"                                         : "",
+               event_where                                     ? event_where                           : "");
+
+       if ( ctx->softLimit )
+       {
+               if ( ctx->hardEventsLimit )
+                       limit = ctx->softLimit < ctx->hardEventsLimit? ctx->softLimit: ctx->hardEventsLimit;
+               else
+                       limit = ctx->softLimit;
+       }
+       else if ( ctx->hardEventsLimit )
+               limit = ctx->hardEventsLimit;
+       else
+               limit = 0;
+
+       i = 0;
+       out = calloc(1, sizeof(*out));
+       do
+       {
+               if ( limit )
+                       trio_asprintf(&q, "%s LIMIT %d, %d", qbase, offset, limit);
+               else if ( !q )
+                       q = qbase;
+
+//             printf("\nquery: %s\n\n", q);
+               ret = edg_wll_ExecStmt(ctx, q, &sh);
+               if ( limit )
+                       free(q);
+               if ( ret < 0 )
+               {
+                       edg_wll_FreeStmt(&sh);
+                       goto cleanup;
+               }
+               if ( ret == 0 )
+               {
+                       limit_loop = 0;
+                       goto limit_cycle_cleanup;
+               }
+               if ( !limit || (ret < limit) )
+                       limit_loop = 0;
+
+               offset += ret;
+               while ( (ret = edg_wll_FetchRow(sh, res)) == sizofa(res) )
+               {
+                       int     n = atoi(res[0]);
+                       free(res[0]);
+
+                       if ( convert_event_head(ctx, res+2, out+i) || edg_wll_get_event_flesh(ctx, n, out+i) )
+                       {
+                               free(res[1]);
+                               memset(out+i, 0, sizeof(*out));
+                               edg_wll_FreeStmt(&sh);
+                               goto cleanup;
+                       }
+
+                       if ( !match_flesh_conditions(out+i,event_conditions) || check_strict_jobid(ctx,out[i].any.jobId) )
+                       {
+                               edg_wll_FreeEvent(out+i);
+                               edg_wll_ResetError(ctx);        /* check_strict_jobid() sets it */
+                               goto fetch_cycle_cleanup;
+                       }
+
+                       if ( !noAuth )
+                       {
+                               if (!ctx->peerName || strcmp(res[1],strmd5(ctx->peerName,NULL))) {
+                                       edg_wll_Acl     acl = NULL;
+                                       char            *jobid = NULL;
+
+                                       ret = edg_wll_GetACL(ctx, out[i].any.jobId, &acl);
+                                       free(jobid);
+                                       if (ret || acl == NULL) {
+                                               eperm = 1;
+                                               edg_wll_FreeEvent(out+i);
+                                               edg_wll_ResetError(ctx); /* XXX: should be reported somewhere at least in debug mode */
+                                               goto fetch_cycle_cleanup;
+                                       }
+
+                                       ret = edg_wll_CheckACL(ctx, acl, EDG_WLL_PERM_READ);
+                                       edg_wll_FreeAcl(acl);
+                                       if (ret) {
+                                               eperm = 1;
+                                               edg_wll_FreeEvent(out+i);
+                                               edg_wll_ResetError(ctx); /* XXX: should be reported somewhere at least in debug mode */
+                                               goto fetch_cycle_cleanup;
+                                       }
+                               }
+                       }
+                       
+                       if ( (ctx->p_query_results != EDG_WLL_QUERYRES_ALL) && limit && (i+1 > limit) )
+                       {
+                               free(res[1]);
+                               memset(out+i, 0, sizeof(*out));
+                               edg_wll_SetError(ctx, E2BIG, "Query result size limit exceeded");
+                               if ( ctx->p_query_results == EDG_WLL_QUERYRES_LIMITED )
+                               {
+                                       limit_loop = 0;
+                                       goto limit_cycle_cleanup;
+                               }
+                               goto cleanup;
+                       }
+
+                       i++;
+                       out = (edg_wll_Event *) realloc(out, (i+1) * sizeof(*out));
+
+fetch_cycle_cleanup:
+                       memset(out+i, 0, sizeof(*out));
+                       free(res[1]);
+               }
+limit_cycle_cleanup:
+               edg_wll_FreeStmt(&sh);
+       } while ( limit_loop );
+
+       if ( i == 0 && eperm )
+               edg_wll_SetError(ctx, EPERM, "matching events found but authorization failed");
+       else if ( i == 0 )
+               edg_wll_SetError(ctx, ENOENT, "no matching events found");
+       else
+       {
+               edg_wll_SortEvents(out);
+               *events = out;
+               out = NULL;
+       }
+
+cleanup:
+       if ( out )
+       {
+               for ( i = 0; out[i].type; i++ )
+                       edg_wll_FreeEvent(out+i);
+               free(out);
+       }
+       free(qbase);
+       free(job_where);
+       free(event_where);
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_QueryJobsServer(
+               edg_wll_Context ctx,
+               const edg_wll_QueryRec **conditions,
+               int     flags,
+               edg_wlc_JobId **jobs,
+               edg_wll_JobStat **states)
+{
+       char                       *job_where = NULL,
+                                          *state_where = NULL,
+                                          *tags_where = NULL,
+                                          *q = NULL,
+                                          *qbase = NULL,
+                                          *res[3];
+       edg_wlc_JobId      *jobs_out = NULL;
+       edg_wll_JobStat    *states_out = NULL;
+       edg_wll_Stmt            sh;
+       int                                     i = 0,
+                                               ret = 0,
+                                               eperm = 0,
+                                               limit = 0, offset = 0,
+                                               limit_loop = 1;
+
+
+       memset(res,0,sizeof res);
+       edg_wll_ResetError(ctx);
+
+       if ( !conditions )
+       {
+               edg_wll_SetError(ctx, EINVAL, "empty condition list");
+               goto cleanup;
+       }
+
+       if ( (ctx->p_query_results == EDG_WLL_QUERYRES_ALL) &&
+                       (!conditions[0] || conditions[1] ||
+                       (conditions[0][0].attr != EDG_WLL_QUERY_ATTR_OWNER) ||
+                       (conditions[0][1].attr != EDG_WLL_QUERY_ATTR_UNDEF)) )
+       {
+               edg_wll_SetError(ctx, EINVAL, "Invalid parameter EDG_WLL_PARAM_QUERY_RESULTS");
+               goto cleanup;
+       }
+
+       if ( (!ctx->noIndex && check_job_query_index(ctx, conditions)) || check_strict_jobid_cond(ctx,conditions))
+               goto cleanup;
+
+       if ( !(job_where = jc_to_head_where(ctx, conditions, &i)) )
+               goto cleanup;
+
+       if ( (i & FL_SEL_STATUS) )
+               trio_asprintf(&qbase,"SELECT DISTINCT j.dg_jobid,j.userid "
+                                                "FROM jobs j, states s WHERE j.jobid=s.jobid AND %s", job_where);
+       else
+               trio_asprintf(&qbase,"SELECT DISTINCT j.dg_jobid,j.userid "
+                                                "FROM jobs j WHERE %s", job_where);
+
+       if ( ctx->softLimit )
+       {
+               if ( ctx->hardJobsLimit )
+                       limit = ctx->softLimit < ctx->hardJobsLimit? ctx->softLimit: ctx->hardJobsLimit;
+               else
+                       limit = ctx->softLimit;
+       }
+       else if ( ctx->hardJobsLimit )
+               limit = ctx->hardJobsLimit;
+       else
+               limit = 0;
+
+       jobs_out        = calloc(1, sizeof(*jobs_out));
+       states_out      = calloc(1, sizeof(*states_out));
+       i = 0;
+       do
+       {
+               if ( limit )
+                       trio_asprintf(&q, "%s LIMIT %d, %d", qbase, offset, limit);
+               else if ( !q )
+                       q = qbase;
+
+//             printf("\nquery: %s\n\n", q);
+               ret = edg_wll_ExecStmt(ctx, q, &sh);
+               if ( limit )
+                       free(q);
+               if ( ret < 0 )
+               {
+                       edg_wll_FreeStmt(&sh);
+                       goto cleanup;
+               }
+               if ( ret == 0 )
+               {
+                       limit_loop = 0;
+                       goto limit_cycle_cleanup;
+               }
+               if ( !limit || (ret < limit) )
+                       limit_loop = 0;
+
+               offset += ret;
+               while ( (ret=edg_wll_FetchRow(sh,res)) > 0 )
+               {
+                       if ( (ret = edg_wlc_JobIdParse(res[0], jobs_out+i)) )
+                       {       /* unlikely to happen, internal inconsistency */
+                               char    buf[200];
+                               snprintf(buf,sizeof buf,"JobIdParse(%s)",res[0]);
+                               edg_wll_SetError(ctx,ret,buf);
+                               free(res[0]); free(res[1]); free(res[2]);
+                               jobs_out[i]     = NULL;
+                               goto cleanup;
+                       }
+
+                       if ( check_strict_jobid(ctx, jobs_out[i]) )
+                       {
+                               edg_wlc_JobIdFree(jobs_out[i]);
+                               goto fetch_cycle_cleanup;
+                       }
+
+                       if ( edg_wll_JobStatus(ctx, jobs_out[i], flags, &states_out[i]) )
+                       {
+                               edg_wlc_JobIdFree(jobs_out[i]);
+                               if (edg_wll_Error(ctx,NULL,NULL) == EPERM) eperm = 1;
+                               goto fetch_cycle_cleanup;
+                       }
+
+                       if ( !match_status(ctx, states_out+i, conditions) )
+                       {
+                               edg_wlc_JobIdFree(jobs_out[i]);
+                               edg_wll_FreeStatus(states_out+i);
+                               edg_wll_ResetError(ctx);        /* check_strict_jobid() sets it */
+                               goto fetch_cycle_cleanup;
+                       }
+
+#if 0
+                       if ( !ctx->noAuth && (!ctx->peerName || strcmp(res[1], strmd5(ctx->peerName, NULL))) )
+                       {
+                               eperm = 1;
+                               edg_wlc_JobIdFree(jobs_out[i]);
+                               edg_wll_FreeStatus(states_out+i);
+                               goto fetch_cycle_cleanup;
+                       }
+#endif
+
+                       if ( (ctx->p_query_results != EDG_WLL_QUERYRES_ALL) && limit && (i+1 > limit) )
+                       {
+                               edg_wll_SetError(ctx, E2BIG, "Query result size limit exceeded");
+                               free(res[0]); free(res[1]); free(res[2]);
+                               memset(states_out+i, 0, sizeof(*states_out));
+                               jobs_out[i]     = NULL;
+                               if ( ctx->p_query_results == EDG_WLL_QUERYRES_LIMITED )
+                               {
+                                       limit_loop = 0;
+                                       goto limit_cycle_cleanup;
+                               }
+                               goto cleanup;
+                       }
+
+                       i++;
+                       jobs_out        = (edg_wlc_JobId *) realloc(jobs_out, (i+1) * sizeof(*jobs_out));
+                       states_out      = (edg_wll_JobStat *) realloc(states_out, (i+1) * sizeof(*states_out));
+
+fetch_cycle_cleanup:
+                       free(res[0]); free(res[1]); free(res[2]);
+                       memset(states_out+i, 0, sizeof(*states_out));
+                       jobs_out[i]     = NULL;
+               }
+limit_cycle_cleanup:
+               edg_wll_FreeStmt(&sh);
+       } while ( limit_loop );
+
+       if ( eperm && !*jobs_out )
+               edg_wll_SetError(ctx, EPERM, "matching jobs found but authorization failed");
+
+       if ( i && (ret == 0) )
+       {
+               if ( states )
+               {
+                       *states = states_out;
+                       states_out = NULL;
+               }
+               if ( jobs )
+               {
+                       *jobs = jobs_out;
+                       jobs_out = NULL;
+               }
+       }
+
+
+cleanup:
+       free(qbase);
+       free(state_where);
+       free(tags_where);
+       free(job_where);
+
+       if (jobs_out)
+       {
+               for ( i = 0; jobs_out[i]; i++ )
+                       edg_wlc_JobIdFree(jobs_out[i]);
+               free(jobs_out);
+       }
+
+       if (states_out)
+       {
+               for (i=0; states_out[i].state; i++) edg_wll_FreeStatus(states_out+i);
+               free(states_out);
+       }
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+static int check_event_query_index(edg_wll_Context ctx,const edg_wll_QueryRec **jc,const edg_wll_QueryRec **ec)
+{
+       int     i, j;
+
+       if (check_job_query_index(ctx,jc) == 0) return 0;
+       edg_wll_ResetError(ctx);
+
+       if (ec && *ec) for (i=0; ec[i]; i++) for (j=0; ec[i][j].attr; j++) switch (ec[i][j].attr) {
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       return 0;
+               default: break;
+       }
+       return edg_wll_SetError(ctx,EDG_WLL_ERROR_NOINDEX,
+                       is_all_query(jc) ? "\"-all\" queries denied by server configuration" : NULL);
+}
+
+static int check_job_query_index(edg_wll_Context ctx, const edg_wll_QueryRec **jc)
+{
+       int             i, j, jj;
+
+
+       edg_wll_ResetError(ctx);
+
+       if ( !jc || !*jc )
+               return edg_wll_SetError(ctx,EDG_WLL_ERROR_NOINDEX,"unrestricted queries unsupported");
+
+       /*
+        *      First check presense of jobid - Primary key
+        */
+       for ( i = 0; jc[i]; i++ ) for ( j = 0; jc[i][j].attr; j++ )
+               if ( jc[i][j].attr == EDG_WLL_QUERY_ATTR_JOBID ) return 0;
+
+       if ( !ctx->job_index )
+               return edg_wll_SetError(ctx, EDG_WLL_ERROR_NOINDEX, "no indices configured: jobid required in query");
+
+       for ( j = 0; ctx->job_index[j]; j++ )
+       {
+               if ( !ctx->job_index[j][0].attr )
+                       continue;
+
+               if ( ctx->job_index[j][1].attr )
+               {
+                       /*
+                        * check multi comlumn indexes
+                        */
+                       for ( jj = 0; ctx->job_index[j][jj].attr; jj++ )
+                       {
+                               for ( i = 0; jc[i]; i++ )
+                                       if ( !edg_wll_CmpColumn(&ctx->job_index[j][jj], &jc[i][0]) ) break;
+                               if ( !jc[i] )
+                                       break;
+                       }
+                       if ( !ctx->job_index[j][jj].attr )
+                               return 0;
+               }
+               else
+                       for ( i = 0; jc[i]; i++ )
+                               if ( !edg_wll_CmpColumn(&ctx->job_index[j][0], &jc[i][0]) ) return 0;
+       }
+
+       return edg_wll_SetError(ctx,EDG_WLL_ERROR_NOINDEX,
+                       is_all_query(jc) ? "\"-all\" queries denied by server configuration" : NULL);
+}
+
+#define opToString(op)                                                                         \
+       ((op) == EDG_WLL_QUERY_OP_EQUAL ? "=" :                                 \
+               ((op) == EDG_WLL_QUERY_OP_UNEQUAL ? "!=" :                      \
+                       ((op) == EDG_WLL_QUERY_OP_LESS ? "<" : ">" )))
+
+static char *ec_to_head_where(edg_wll_Context ctx,const edg_wll_QueryRec **ec)
+{
+       int                                     n, ct, m;
+       char                            msg[100],
+                                          *out,
+                                          *aux,
+                                          *conds, *retconds,
+                                          *dbt;
+                       
+
+       /*
+        * check correctness only
+        */
+       for ( ct = m = 0; ec[m]; m++ )
+       {
+               for ( n = 0; ec[m][n].attr; n++ ) switch ( ec[m][n].attr )
+               {
+               case EDG_WLL_QUERY_ATTR_HOST:
+                       if ( ec[m][n].op != EDG_WLL_QUERY_OP_EQUAL && ec[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with host attr");
+                               return NULL;
+                       }
+                       ct++;
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_SOURCE:
+                       if ( ec[m][n].op != EDG_WLL_QUERY_OP_EQUAL && ec[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with host attr");
+                               return NULL;
+                       }
+                       ct++;
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+                       if ( ec[m][n].op != EDG_WLL_QUERY_OP_EQUAL && ec[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with usertag attr");
+                               return NULL;
+                       }
+                       ct++;
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_INSTANCE:
+                       if ( ec[m][n].op != EDG_WLL_QUERY_OP_EQUAL && ec[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with instance attr");
+                               return NULL;
+                       }
+                       ct++;
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_EVENT_TYPE:
+               case EDG_WLL_QUERY_ATTR_LEVEL:
+                       /*
+                        *      Any op allowed - but be careful
+                        */
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       /* any operator allowed */
+                       ct++;
+                       break;
+
+               default:
+                       sprintf(msg, "ec_to_head_where(): attr=%d unsupported", ec[m][n].attr);
+                       edg_wll_SetError(ctx, EINVAL, msg);
+                       return NULL;
+               }
+       }
+
+       if ( ct == 0 )
+       {
+               edg_wll_SetError(ctx,EINVAL,"ec_to_head_where(): empty conditions");
+               return NULL;
+       }
+
+       conds = retconds = NULL;
+       for ( m = 0; ec[m]; m++ )
+       {
+               /* 
+                * conditions captured by match_flesh_conditions() have to be skipped here.
+                * with all conditions in same "or clause"
+                */
+               for ( n = 0; ec[m][n].attr; n++ )
+                       if (    (ec[m][n].attr == EDG_WLL_QUERY_ATTR_USERTAG)
+                                || (ec[m][n].attr == EDG_WLL_QUERY_ATTR_INSTANCE) )
+                               break;
+               if ( ec[m][n].attr )
+                       continue;
+
+               for ( n = 0; ec[m][n].attr; n++ ) switch ( ec[m][n].attr )
+               {
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       dbt = edg_wll_TimeToDB(ec[m][n].value.t.tv_sec);
+                       if ( conds )
+                       {
+                               if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                               {
+                                       trio_asprintf(&aux, "%s", dbt);
+                                       dbt = edg_wll_TimeToDB(ec[m][n].value2.t.tv_sec);
+                                       trio_asprintf(&out, "%s OR (e.time_stamp >= %s AND e.time_stamp <= %s)", conds, aux, dbt);
+                                       free(aux);
+                               }
+                               else
+                                       trio_asprintf(&out, "%s OR e.time_stamp %s %s", conds, opToString(ec[m][n].op), dbt);
+                               free(conds);
+                               conds = out;
+                       }
+                       else if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                       {
+                               trio_asprintf(&aux, "%s", dbt);
+                               dbt = edg_wll_TimeToDB(ec[m][n].value2.t.tv_sec);
+                               trio_asprintf(&conds, "(e.time_stamp >= %s AND e.time_stamp <= %s)", aux, dbt);
+                               free(aux);
+                       }
+                       else
+                               trio_asprintf(&conds, "e.time_stamp %s %s", opToString(ec[m][n].op), dbt);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_LEVEL:
+                       if ( conds )
+                       {
+                               if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                                       trio_asprintf(&out, "%s OR (e.level >= %d AND e.level <= %d)", conds, ec[m][n].value.i, ec[m][n].value2.i);
+                               else
+                                       trio_asprintf(&out, "%s OR e.level %s %d", conds, opToString(ec[m][n].op), ec[m][n].value.i);
+                               free(conds); conds = out;
+                       }
+                       else
+                               if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                                       trio_asprintf(&conds, "(e.level >= %d AND e.level <= %d)", ec[m][n].value.i, ec[m][n].value2.i);
+                               else
+                                       trio_asprintf(&conds, "e.level %s %d", opToString(ec[m][n].op), ec[m][n].value.i);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_SOURCE:
+                       aux = edg_wll_SourceToString(ec[m][n].value.i);
+                       if ( conds )
+                       {
+                               trio_asprintf(&out, "%s OR e.prog %s '%|Ss'", conds, opToString(ec[m][n].op), aux);
+                               free(conds); conds = out;
+                       }
+                       else
+                               trio_asprintf(&conds, "e.prog %s '%|Ss'", opToString(ec[m][n].op), aux);
+                       free(aux);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_HOST:
+                       if ( conds )
+                       {
+                               trio_asprintf(&out, "%s OR e.host %s '%|Ss'", conds, opToString(ec[m][n].op), ec[m][n].value.c);
+                               free(conds); conds = out;
+                       }
+                       else
+                               trio_asprintf(&conds, "e.host %s '%|Ss'", opToString(ec[m][n].op), ec[m][n].value.c);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_EVENT_TYPE:
+                       if ( conds )
+                       {
+                               if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                                       trio_asprintf(&out, "%s OR (e.code >= %d AND e.code <= %d)", conds, ec[m][n].value.i, ec[m][n].value2.i);
+                               else
+                                       trio_asprintf(&out, "%s OR e.code %s %d", conds, opToString(ec[m][n].op), ec[m][n].value.i);
+                               free(conds); conds = out;
+                       }
+                       else
+                               if ( ec[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                                       trio_asprintf(&conds, "(e.code >= %d AND e.code <= %d)", ec[m][n].value.i, ec[m][n].value2.i);
+                               else
+                                       trio_asprintf(&conds, "e.code %s %d", opToString(ec[m][n].op), ec[m][n].value.i);
+                       break;
+
+               default:
+                       return NULL;
+               }
+
+               if ( !conds )
+                       continue;       
+
+               if ( retconds )
+               {
+                       trio_asprintf(&out, "%s AND (%s)", retconds, conds);
+                       free(retconds); retconds = out;
+                       free(conds); conds = NULL;
+               }
+               else
+               {
+                       trio_asprintf(&retconds, "(%s)", conds);
+                       free(conds); conds = NULL;
+               }
+       }
+
+       return retconds;
+}
+
+static int is_indexed(const edg_wll_QueryRec *cond, const edg_wll_Context ctx)
+{
+       int             i, j;
+
+       if ( !(ctx->job_index) )
+               return 0;
+
+       for ( i = 0; ctx->job_index[i]; i++ ) for ( j = 0; ctx->job_index[i][j].attr; j++ )
+               if ( !edg_wll_CmpColumn(&ctx->job_index[i][j], cond) ) return 1;
+       
+       return 0;
+}
+
+/*
+ *     use where_flags to recognize which table to jois in SQL select
+ *     
+ */
+static char *jc_to_head_where(
+                               edg_wll_Context ctx,
+                               const edg_wll_QueryRec **jc,
+                               int *where_flags)
+{
+       int             ct, n, m;
+       char   *aux,
+                  *tmps,
+                  *dbt,
+                  *cname = NULL,
+                       msg[100];
+       char   *conds, *retconds;
+
+
+       retconds = conds = NULL;
+
+       /*
+        * check correctness only
+        */
+       for ( ct = m = 0; jc[m]; m++ ) for ( n = 0; jc[m][n].attr; n++ )
+       {
+               if (   (jc[m][0].attr != jc[m][n].attr)
+                       || (   (jc[m][n].attr == EDG_WLL_QUERY_ATTR_USERTAG)
+                               && strcmp(jc[m][0].attr_id.tag, jc[m][n].attr_id.tag)) )
+               {
+                       edg_wll_SetError(ctx, EINVAL, "only same attribute types supported in 'or' expressions");
+                       return NULL;
+               }
+
+               switch ( jc[m][n].attr )
+               {
+               case EDG_WLL_QUERY_ATTR_JOBID:
+                       ct++;
+                       if ( jc[m][n].op != EDG_WLL_QUERY_OP_EQUAL && jc[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with jobid");
+                               return NULL;
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_OWNER:
+                       ct++;
+                       if ( jc[m][n].op != EDG_WLL_QUERY_OP_EQUAL && jc[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with job owner");
+                               return NULL;
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_LOCATION:
+                       ct++;
+                       if ( jc[m][n].op != EDG_WLL_QUERY_OP_EQUAL && jc[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with job location");
+                               return NULL;
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_DESTINATION:
+                       ct++;
+                       if ( jc[m][n].op != EDG_WLL_QUERY_OP_EQUAL && jc[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with job destinations");
+                               return NULL;
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_PARENT:
+                       ct++;
+                       if ( jc[m][n].op != EDG_WLL_QUERY_OP_EQUAL && jc[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with job parent");
+                               return NULL;
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                       ct++;
+                       if ( jc[m][n].op != EDG_WLL_QUERY_OP_EQUAL && jc[m][n].op != EDG_WLL_QUERY_OP_UNEQUAL )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "only `=' and '!=' supported with resubmitted attr");
+                               return NULL;
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       if ( jc[m][n].attr_id.state == EDG_WLL_JOB_UNDEF )
+                       {
+                               edg_wll_SetError(ctx, EINVAL, "Time attribut have to be associated with status specification");
+                               return NULL;
+                       }
+                       ct++;
+                       break;
+               case EDG_WLL_QUERY_ATTR_DONECODE:
+               case EDG_WLL_QUERY_ATTR_EXITCODE:
+               case EDG_WLL_QUERY_ATTR_STATUS:
+                       ct++;
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+                       ct++;
+                       break;
+
+               default:
+                       sprintf(msg, "jc_to_head_where(): attr=%d unsupported", jc[m][n].attr);
+                       edg_wll_SetError(ctx, EINVAL, msg);
+                       return NULL;
+               }
+       }
+
+       if ( ct == 0 )
+       {
+               edg_wll_SetError(ctx, EINVAL, "jc_to_head_where(): empty conditions");
+               return NULL;
+       }
+
+       /*
+        * Now process conversion
+        */
+       for ( m = 0; jc[m]; m++ )
+       {
+               for ( n = 0; jc[m][n].attr; n++ ) switch (jc[m][n].attr)
+               {
+               case EDG_WLL_QUERY_ATTR_TIME:
+                       if (   !is_indexed(&(jc[m][n]), ctx)
+                               || !(cname = edg_wll_QueryRecToColumn(&(jc[m][n]))) )
+                               break;
+
+                       *where_flags |= FL_SEL_STATUS;
+
+                       dbt = edg_wll_TimeToDB(jc[m][n].value.t.tv_sec);
+                       if ( conds )
+                       {
+                               if ( jc[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                               {
+                                       trio_asprintf(&aux, "%s", dbt);
+                                       dbt = edg_wll_TimeToDB(jc[m][n].value2.t.tv_sec);
+                                       trio_asprintf(&tmps, "%s OR (s.%s >= %s AND s.%s <= %s)", conds, cname, aux, cname, dbt);
+                                       free(aux);
+                               }
+                               else
+                                       trio_asprintf(&tmps, "%s OR s.%s %s s.%s", conds, cname, opToString(jc[m][n].op), dbt);
+
+                               free(conds);
+                               conds = tmps;
+                       }
+                       else if ( jc[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                       {
+                               trio_asprintf(&aux, "%s", dbt);
+                               dbt = edg_wll_TimeToDB(jc[m][n].value2.t.tv_sec);
+                               trio_asprintf(&conds, "(%s >= s.%s AND s.%s <= %s)", cname, aux, cname, dbt);
+                               free(aux);
+                       }
+                       else
+                               trio_asprintf(&conds, "s.%s %s %s", cname, opToString(jc[m][n].op), dbt);
+
+                       free(cname);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_JOBID:
+                       *where_flags |= FL_SEL_JOB;
+                       aux = edg_wlc_JobIdGetUnique(jc[m][n].value.j);
+                       if ( conds )
+                       {
+                               trio_asprintf(&tmps, "%s OR j.jobid%s'%|Ss'", conds, opToString(jc[m][n].op), aux);
+                               free(conds); conds = tmps;
+                       }
+                       else
+                               trio_asprintf(&conds, "j.jobid%s'%|Ss'", opToString(jc[m][n].op), aux);
+                       free(aux);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_PARENT:
+                       if (   !is_indexed(&(jc[m][n]), ctx)
+                               || !(cname = edg_wll_QueryRecToColumn(&(jc[m][n]))) )
+                               break;
+
+                       *where_flags |= FL_SEL_STATUS;
+                       aux = edg_wlc_JobIdGetUnique(jc[m][n].value.j);
+                       if ( conds )
+                       {
+                               trio_asprintf(&tmps, "%s OR s.%s%s'%|Ss'", conds, cname, opToString(jc[m][n].op), aux);
+                               free(conds); conds = tmps;
+                       }
+                       else
+                               trio_asprintf(&conds, "s.%s%s'%|Ss'", cname, opToString(jc[m][n].op), aux);
+                       free(aux);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_OWNER:
+                       if (   !is_indexed(&(jc[m][n]), ctx)
+                               || !(cname = edg_wll_QueryRecToColumn(&(jc[m][n]))) )
+                               break;
+
+                       if ( !jc[m][n].value.c && !ctx->peerName ) 
+                       { 
+                               edg_wll_SetError(ctx, EPERM, "jc_to_head_where(): ctx->peerName empty");
+                               free(cname); free(conds); free(retconds);
+                               return NULL;
+                       }       
+
+                       *where_flags |= FL_SEL_STATUS;
+                       if ( conds )
+                       {
+                               if ( jc[m][n].value.c ) 
+                                       trio_asprintf(&tmps, "%s OR s.%s%s'%|Ss'", conds, cname, opToString(jc[m][n].op), jc[m][n].value.c);
+                               else
+                                       trio_asprintf(&tmps, "%s OR s.%s%s'%|Ss'", conds, cname, opToString(jc[m][n].op), ctx->peerName);
+                               free(conds); conds = tmps;
+                       }
+                       else
+                       {
+                               if ( jc[m][n].value.c ) 
+                                       trio_asprintf(&conds, "s.%s%s'%|Ss'", cname, opToString(jc[m][n].op), jc[m][n].value.c);
+                               else
+                                       trio_asprintf(&conds, "s.%s%s'%|Ss'", cname, opToString(jc[m][n].op), ctx->peerName);
+                       }
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_DONECODE:
+               case EDG_WLL_QUERY_ATTR_EXITCODE:
+               case EDG_WLL_QUERY_ATTR_STATUS:
+                       if (   !is_indexed(&(jc[m][n]), ctx)
+                               || !(cname = edg_wll_QueryRecToColumn(&(jc[m][n]))) )
+                               break;
+
+                       *where_flags |= FL_SEL_STATUS;
+                       if ( conds )
+                       {
+                               if ( jc[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                                       trio_asprintf(&tmps, "%s OR (s.%s >= %d AND s.%s <= %d)", conds, cname, jc[m][n].value.i, cname, jc[m][n].value2.i);
+                               else
+                                       trio_asprintf(&tmps, "%s OR s.%s %s %d", conds, cname, opToString(jc[m][n].op), jc[m][n].value.i);
+                               free(conds); conds = tmps;
+                       }
+                       else
+                               if ( jc[m][n].op == EDG_WLL_QUERY_OP_WITHIN )
+                                       trio_asprintf(&conds, "(s.%s >= %d AND s.%s <= %d)", cname, jc[m][n].value.i, cname, jc[m][n].value2.i);
+                               else
+                                       trio_asprintf(&conds, "s.%s %s %d", cname, opToString(jc[m][n].op),jc[m][n].value.i);
+
+                       free(cname);
+                       break;
+
+               case EDG_WLL_QUERY_ATTR_DESTINATION:
+               case EDG_WLL_QUERY_ATTR_LOCATION:
+               case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+               case EDG_WLL_QUERY_ATTR_USERTAG:
+                       if (   !is_indexed(&(jc[m][n]), ctx)
+                               || !(cname = edg_wll_QueryRecToColumn(&(jc[m][n]))) )
+                               break;
+
+                       *where_flags |= FL_SEL_STATUS;
+                       if ( conds )
+                       {
+                               trio_asprintf(&tmps, "%s OR s.%s%s'%s'", conds, cname, opToString(jc[m][n].op), jc[m][n].value.c);
+                               free(conds); conds = tmps;
+                       }
+                       else
+                               trio_asprintf(&conds, "s.%s%s'%s'", cname, opToString(jc[m][n].op), jc[m][n].value.c);
+
+                       free(cname);
+                       break;
+
+               default:
+                       /* this may never occure, but keep compiler happy */
+                       break;
+               }
+
+               if ( !conds ) continue;
+
+               if ( retconds )
+               {
+                       trio_asprintf(&tmps, "%s AND (%s)", retconds, conds);
+                       free(retconds); retconds = tmps;
+                       free(conds); conds = NULL;
+               }
+               else
+               {
+                       trio_asprintf(&retconds, "(%s)", conds);
+                       free(conds); conds = NULL;
+               }
+       }
+
+       return retconds;
+}
+
+
+static int match_flesh_conditions(const edg_wll_Event *e,const edg_wll_QueryRec **ec)
+{
+       int                     i, j;
+
+
+       if ( !ec )
+               return 1;
+
+       for ( i = 0; ec[i]; i++ )
+       {
+               j = 0;
+               while (    (ec[i][j].attr)
+                               && (ec[i][j].attr != EDG_WLL_QUERY_ATTR_USERTAG)
+                               && (ec[i][j].attr != EDG_WLL_QUERY_ATTR_INSTANCE) ) j++;
+               if ( !ec[i][j].attr )
+                       continue;
+
+               for ( j = 0; ec[i][j].attr; j++ )
+               {
+                       if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_TIME )
+                       {
+                               if (   ((ec[i][j].op == EDG_WLL_QUERY_OP_EQUAL) && (e->any.timestamp.tv_sec == ec[i][j].value.t.tv_sec))
+                                       || ((ec[i][j].op == EDG_WLL_QUERY_OP_LESS) && (e->any.timestamp.tv_sec < ec[i][j].value.t.tv_sec))
+                                       || ((ec[i][j].op == EDG_WLL_QUERY_OP_GREATER) && (e->any.timestamp.tv_sec < ec[i][j].value.t.tv_sec))
+                                       || (    (ec[i][j].op == EDG_WLL_QUERY_OP_WITHIN)
+                                               && (e->any.timestamp.tv_sec >= ec[i][j].value.t.tv_sec)
+                                               && (e->any.timestamp.tv_sec <= ec[i][j].value2.t.tv_sec)) )
+                                       break;
+                       }
+                       else if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_SOURCE )
+                       {
+                               if ( e->any.source == EDG_WLL_SOURCE_NONE )
+                                       continue;
+                               if (   ((ec[i][j].op == EDG_WLL_QUERY_OP_EQUAL) && (e->any.source == ec[i][j].value.i))
+                                       || ((ec[i][j].op == EDG_WLL_QUERY_OP_LESS) && (e->any.source < ec[i][j].value.i))
+                                       || ((ec[i][j].op == EDG_WLL_QUERY_OP_GREATER) && (e->any.source < ec[i][j].value.i))
+                                       || (   (ec[i][j].op == EDG_WLL_QUERY_OP_WITHIN)
+                                               && (e->any.source >= ec[i][j].value.i)
+                                               && (e->any.source <= ec[i][j].value2.i)) )
+                                       break;
+                       }
+                       else if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_LEVEL )
+                       {
+                               if (   ((ec[i][j].op == EDG_WLL_QUERY_OP_EQUAL) && (e->any.level == ec[i][j].value.i))
+                                       || ((ec[i][j].op == EDG_WLL_QUERY_OP_LESS) && (e->any.level < ec[i][j].value.i))
+                                       || ((ec[i][j].op == EDG_WLL_QUERY_OP_GREATER) && (e->any.level < ec[i][j].value.i))
+                                       || (   (ec[i][j].op == EDG_WLL_QUERY_OP_WITHIN)
+                                               && (e->any.level >= ec[i][j].value.i)
+                                               && (e->any.level <= ec[i][j].value2.i)) )
+                                       break;
+                       }
+                       else if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_HOST )
+                       {
+                               if ( !strcmp(ec[i][j].value.c, e->any.host) )
+                                       break;
+                       }
+                       else if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_EVENT_TYPE )
+                       {
+                               if ( e->any.type == ec[i][j].value.i )
+                                       break;
+                       }
+                       else if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_USERTAG )
+                       {
+                               if (    !strcmp(ec[i][j].attr_id.tag,e->userTag.name) 
+                                        && cmp_string(e->userTag.value,ec[i][j].op,ec[i][j].value.c))
+                                       break;
+                       }
+                       else if ( ec[i][j].attr == EDG_WLL_QUERY_ATTR_INSTANCE )
+                       {
+                               if (    e->any.src_instance
+                                        && cmp_string(ec[i][j].value.c, ec[i][j].op,  e->any.src_instance) )
+                                       break;
+                       }
+               }
+               if ( !ec[i][j].attr )
+                       /*
+                        *      No condition in "or" clause is valid
+                        */
+                       return 0;
+       }
+
+       return 1;
+}
+
+/* XXX: has to match exactly the main query in edg_wll_QueryEvents 
+ *      and similar one in srv_purge.c 
+ */
+int convert_event_head(edg_wll_Context ctx,char **f,edg_wll_Event *e)
+{
+       int     ret,i;
+
+       memset(e,0,sizeof *e);
+       edg_wll_ResetError(ctx);
+
+
+       if ((ret=edg_wlc_JobIdParse(f[0],&e->any.jobId))) {
+               edg_wll_SetError(ctx,-ret,"edg_wlc_JobIdParse()");
+               goto err;
+       }
+
+       e->type = atoi(f[1]);
+       free(f[1]); f[1] = NULL;
+       
+       e->any.source = edg_wll_StringToSource(f[2]); 
+       free(f[2]); f[2] = NULL;
+       
+       e->any.host = f[3];
+       f[3] = NULL;
+       
+       e->any.user = f[4];
+       f[4] = NULL;
+       
+       e->any.timestamp.tv_sec = edg_wll_DBToTime(f[5]);
+       free(f[5]); f[5] = NULL;
+       
+       e->any.timestamp.tv_usec = atoi(f[6]);
+       free(f[6]); f[6] = NULL;
+
+       e->any.level = atoi(f[7]); 
+       free(f[7]); f[7] = NULL;
+
+       e->any.arrived.tv_sec = edg_wll_DBToTime(f[8]); 
+       e->any.arrived.tv_usec = 0;
+       free(f[8]); f[8] = NULL;
+
+       return 0;
+
+err:
+       edg_wll_FreeEvent(e);
+       memset(e,0,sizeof *e);
+       for (i=0; i<9; i++) free(f[i]);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+
+
+/* FIXME: other op's, ORed conditions */
+int match_status(edg_wll_Context ctx, const edg_wll_JobStat *stat, const edg_wll_QueryRec **conds)
+{
+       int             i, j, n;
+       char   *s, *s1;
+
+
+       if ( !conds ) return 1;
+
+       for ( i = 0; conds[i]; i++ )
+       {
+               for ( j = 0; conds[i][j].attr; j++ )
+               {
+                       if ( is_indexed(&(conds[i][j]), ctx) ) goto or_satisfied;
+
+                       switch ( conds[i][j].attr )
+                       {
+                       case EDG_WLL_QUERY_ATTR_STATUS: 
+                               switch ( conds[i][j].op )
+                               {
+                               case EDG_WLL_QUERY_OP_EQUAL:
+                                       if ( conds[i][j].value.i == stat->state ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_UNEQUAL:
+                                       if ( conds[i][j].value.i != stat->state ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_LESS:
+                                       if ( conds[i][j].value.i > stat->state ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_GREATER:
+                                       if ( conds[i][j].value.i < stat->state ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_WITHIN:
+                                       if (   conds[i][j].value.i <= stat->state
+                                               && conds[i][j].value2.i >= stat->state ) goto or_satisfied;
+                                       break;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_RESUBMITTED:
+                               if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) {
+                                       if ( conds[i][j].value.i == stat->resubmitted ) goto or_satisfied;
+                               } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL )
+                                       if ( conds[i][j].value.i != stat->resubmitted ) goto or_satisfied;
+                               break;
+                       case EDG_WLL_QUERY_ATTR_DONECODE:
+                               switch ( conds[i][j].op )
+                               {
+                               case EDG_WLL_QUERY_OP_EQUAL:
+                                       if ( conds[i][j].value.i == stat->done_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_UNEQUAL:
+                                       if ( conds[i][j].value.i != stat->done_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_LESS:
+                                       if ( conds[i][j].value.i > stat->done_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_GREATER:
+                                       if ( conds[i][j].value.i < stat->done_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_WITHIN:
+                                       if (   conds[i][j].value.i <= stat->done_code
+                                               && conds[i][j].value2.i >= stat->done_code ) goto or_satisfied;
+                                       break;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_EXITCODE:
+                               switch ( conds[i][j].op )
+                               {
+                               case EDG_WLL_QUERY_OP_EQUAL:
+                                       if ( conds[i][j].value.i == stat->exit_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_UNEQUAL:
+                                       if ( conds[i][j].value.i != stat->exit_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_LESS:
+                                       if ( conds[i][j].value.i > stat->exit_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_GREATER:
+                                       if ( conds[i][j].value.i < stat->exit_code ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_WITHIN:
+                                       if (   conds[i][j].value.i <= stat->exit_code
+                                               && conds[i][j].value2.i >= stat->exit_code ) goto or_satisfied;
+                                       break;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_OWNER:
+                               if (stat->owner) {
+                                       if (conds[i][j].value.c) {
+                                               if (!strcmp(conds[i][j].value.c, stat->owner) ) {
+                                                       if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                               } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+                                       } else if (ctx->peerName) {
+                                               if (!strcmp(ctx->peerName, stat->owner) ) {
+                                                       if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                               } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+                                       }
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_LOCATION:
+                               if ( stat->location )
+                               {
+                                       if ( !strcmp(conds[i][j].value.c, stat->location) ) {
+                                               if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                       } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_DESTINATION:
+                               if ( stat->destination )
+                               {
+                                       if ( !strcmp(conds[i][j].value.c, stat->destination) ) {
+                                               if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                       } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_JOBID:
+                               if ( !stat->jobId )
+                                       break;
+                               s = edg_wlc_JobIdUnparse(stat->jobId);
+                               s1 = edg_wlc_JobIdUnparse(conds[i][j].value.j);
+                               if ( s && s1 )
+                               {
+                                       int r = strcmp(s1, s);
+                                       free(s); free(s1);
+                                       if ( !r ) {
+                                               if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                       } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_PARENT:
+                               if ( !stat->parent_job )
+                                       break;
+                               s = edg_wlc_JobIdUnparse(stat->parent_job);
+                               s1 = edg_wlc_JobIdUnparse(conds[i][j].value.j);
+                               if ( s && s1 )
+                               {
+                                       int r = strcmp(s1, s);
+                                       free(s); free(s1);
+                                       if ( !r ) {
+                                               if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                       } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+                               }
+                               break;
+                       case EDG_WLL_QUERY_ATTR_USERTAG:
+                               if ( !stat->user_tags )
+                               {
+                                       if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL )
+                                               goto or_satisfied;
+
+                                       break;
+                               }
+                               for ( n = 0; stat->user_tags[n].tag; n++ )
+                                       if ( !strcasecmp(conds[i][j].attr_id.tag, stat->user_tags[n].tag) )
+                                       {
+                                               if ( !strcmp(conds[i][j].value.c, stat->user_tags[n].value) ) {
+                                                       if ( conds[i][j].op == EDG_WLL_QUERY_OP_EQUAL ) goto or_satisfied;
+                                               } else if ( conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL ) goto or_satisfied;
+
+                                               break;
+                                       }
+                               if ( !stat->user_tags[n].tag && conds[i][j].op == EDG_WLL_QUERY_OP_UNEQUAL )
+                                       goto or_satisfied;
+                               break;
+                       case EDG_WLL_QUERY_ATTR_TIME:
+                               if ( !stat->stateEnterTimes || !stat->stateEnterTimes[1+conds[i][j].attr_id.state] )
+                                       break;
+                               switch ( conds[i][j].op )
+                               {
+                               case EDG_WLL_QUERY_OP_EQUAL:
+                                       if ( conds[i][j].value.t.tv_sec == stat->stateEnterTimes[1+conds[i][j].attr_id.state] ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_UNEQUAL:
+                                       if ( conds[i][j].value.t.tv_sec != stat->stateEnterTimes[1+conds[i][j].attr_id.state] ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_LESS:
+                                       if ( conds[i][j].value.t.tv_sec > stat->stateEnterTimes[1+conds[i][j].attr_id.state] ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_GREATER:
+                                       if ( conds[i][j].value.t.tv_sec < stat->stateEnterTimes[1+conds[i][j].attr_id.state] ) goto or_satisfied;
+                                       break;
+                               case EDG_WLL_QUERY_OP_WITHIN:
+                                       if (   conds[i][j].value.t.tv_sec <= stat->stateEnterTimes[1+conds[i][j].attr_id.state]
+                                               && conds[i][j].value2.t.tv_sec >= stat->stateEnterTimes[1+conds[i][j].attr_id.state] ) goto or_satisfied;
+                                       break;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               }
+
+                       /*
+                        *      No one condition in "or clause satisfied
+                        */
+               return 0;
+
+or_satisfied:
+                // just for escaping from nested cycles
+               ;       /* prevent compiler to complain */
+       }
+
+       return 1;
+}
+
+static int cmp_string(const char *s1,edg_wll_QueryOp op,const char *s2)
+{
+       switch (op) {
+               case EDG_WLL_QUERY_OP_EQUAL:    return !strcmp(s1,s2);
+               case EDG_WLL_QUERY_OP_LESS:     return strcmp(s1,s2)<0;
+               case EDG_WLL_QUERY_OP_GREATER:  return strcmp(s1,s2)>0;
+               default: return 0;
+       }
+       return 0;
+}
+
+
+int check_strict_jobid(edg_wll_Context ctx, const edg_wlc_JobId job)
+{
+       char    *job_host;
+       unsigned int    job_port;
+
+       edg_wll_ResetError(ctx);
+
+       /* Allow all jobids when server name is not set. */
+       if (ctx->srvName == NULL) return edg_wll_Error(ctx,NULL,NULL);
+
+       edg_wlc_JobIdGetServerParts(job,&job_host,&job_port);
+
+       if (strcasecmp(job_host,ctx->srvName) || job_port != ctx->srvPort)
+       {
+               char    *jobid,msg[300];
+
+               jobid = edg_wlc_JobIdUnparse(job);
+               snprintf(msg,sizeof msg,"%s: does not match server address",jobid);
+               msg[sizeof msg - 1] = 0;
+               edg_wll_SetError(ctx,EINVAL,msg);
+               free(jobid);
+       }
+
+       free(job_host);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static int check_strict_jobid_cond(edg_wll_Context ctx, const edg_wll_QueryRec **cond)
+{
+       int     i,j,ret;
+
+       if (!cond) return edg_wll_ResetError(ctx);
+       for (i=0; cond[i]; i++) for (j=0; cond[i][j].attr; j++) 
+               if (cond[i][j].attr == EDG_WLL_QUERY_ATTR_JOBID &&
+                       (ret = check_strict_jobid(ctx,cond[i][j].value.j)))
+                               return ret;
+
+       return 0;
+}
+
+static int is_all_query(const edg_wll_QueryRec **jc)
+{
+       if (!jc || !*jc) return 1;
+
+       if (jc[0][0].attr == EDG_WLL_QUERY_ATTR_OWNER && 
+           jc[0][0].op == EDG_WLL_QUERY_OP_EQUAL &&
+           !jc[0][1].attr && !jc[1]) return 1;
+
+       return 0;
+}
diff --git a/org.glite.lb.server/src/query.h b/org.glite.lb.server/src/query.h
new file mode 100644 (file)
index 0000000..fb421e4
--- /dev/null
@@ -0,0 +1,3 @@
+int convert_event_head(edg_wll_Context,char **,edg_wll_Event *);
+int check_strict_jobid(edg_wll_Context, const edg_wlc_JobId);
+int match_status(edg_wll_Context, const edg_wll_JobStat *stat,const edg_wll_QueryRec **conditions);
diff --git a/org.glite.lb.server/src/request.c b/org.glite.lb.server/src/request.c
new file mode 100644 (file)
index 0000000..0424043
--- /dev/null
@@ -0,0 +1,96 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "glite/lb/il_string.h"
+#include "glite/lb/context-int.h"
+
+#include "store.h"
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+    
+int 
+handle_request(edg_wll_Context ctx,char *buf, int len UNUSED_VAR)
+{
+  char *p = buf;
+  char *event, *ucs;
+  int ret;
+
+  edg_wll_ResetError(ctx);
+
+  p = get_string(p, &ucs);
+  if(p == NULL) return edg_wll_SetError(ctx,EDG_WLL_IL_PROTO,"reading UCS");
+
+  p = get_string(p, &event);
+  if(p == NULL) {
+    edg_wll_SetError(ctx,EDG_WLL_IL_PROTO,"reading event string");
+    if(ucs) free(ucs);
+    return EDG_WLL_IL_PROTO;
+  }
+
+  ret = db_store(ctx,ucs, event);
+
+  if(ucs)
+    free(ucs);
+  if(event)
+    free(event);
+
+  return(ret);
+}
+
+
+int 
+create_reply(const edg_wll_Context ctx,char *buf, int max_len)
+{
+  int len, err_code, err_code_min;
+  char *p;
+  char *err_msg;
+
+  err_code_min = 0;
+
+  switch(edg_wll_Error(ctx,NULL,&err_msg)) {
+
+  case 0:
+    err_code = LB_OK;
+    break;
+
+  case ENOMEM:
+    err_code = LB_NOMEM;
+    break;
+
+  case EDG_WLL_IL_PROTO:
+    err_code = LB_PROTO;
+    break;
+    
+  default:
+    err_code = LB_DBERR;
+    err_code_min = edg_wll_Error(ctx,NULL,NULL);
+    break;
+
+  } 
+
+  if (!err_msg) err_msg=strdup("OK");
+  
+  len = 17 + len_int(err_code) + len_int(err_code_min) + len_string(err_msg);
+  if(len > max_len) {
+    free(err_msg);
+    return(0);
+  }
+
+  snprintf(buf, max_len, "%16d\n", len - 17);
+  p = buf + 17;
+  p = put_int(p, err_code);
+  p = put_int(p, err_code_min);
+  p = put_string(p, err_msg);
+  free(err_msg);
+  
+  return(len);
+}
+
+
diff --git a/org.glite.lb.server/src/server_state.c b/org.glite.lb.server/src/server_state.c
new file mode 100644 (file)
index 0000000..d2bf8f0
--- /dev/null
@@ -0,0 +1,54 @@
+#ident "$Header$"
+
+#include "glite/lb/trio.h"
+#include "glite/lb/context-int.h"
+
+#include "lbs_db.h"
+#include "server_state.h"
+
+int edg_wll_GetServerState(edg_wll_Context ctx,const char *name,char **val)
+{
+       char    *stmt = NULL;
+       edg_wll_Stmt    q = NULL;
+
+
+       trio_asprintf(&stmt,"select value from server_state "
+                       "where prefix = 'https://%|Ss:%d' and name = '%|Ss'",
+                       ctx->srvName,ctx->srvPort,name);
+
+       switch (edg_wll_ExecStmt(ctx,stmt,&q)) {
+               case 0: edg_wll_SetError(ctx,ENOENT,name); break;
+               case -1: break;
+               default: edg_wll_FetchRow(q,val); break;
+       }
+
+       edg_wll_FreeStmt(&q);
+       free(stmt);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_SetServerState(edg_wll_Context ctx,const char *name,const char *val)
+{
+       char    *stmt = NULL;
+
+       trio_asprintf(&stmt,"insert into server_state (prefix,name,value) "
+                       "values ('https://%|Ss:%d','%|Ss','%|Ss')",
+                       ctx->srvName,ctx->srvPort,name,val);
+
+       switch(edg_wll_ExecStmt(ctx,stmt,NULL)) {
+               case 1: break;
+               case -1: if (edg_wll_Error(ctx,NULL,NULL) == EEXIST) {
+                                free(stmt);
+                                trio_asprintf(&stmt,"update server_state set value = '%|Ss' "
+                                                "where prefix = 'https://%|Ss:%d' "
+                                                "and name = '%|Ss'",
+                                                val,ctx->srvName,ctx->srvPort,name);
+                                edg_wll_ExecStmt(ctx,stmt,NULL);
+                        }
+                        break;
+
+               default: abort();
+       }
+       free(stmt);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.server/src/server_state.h b/org.glite.lb.server/src/server_state.h
new file mode 100644 (file)
index 0000000..029fb7c
--- /dev/null
@@ -0,0 +1,10 @@
+#ifndef _EDG_WORKLOAD_LOGGING_LBSERVER_H_
+#define EDG_WLL_STATE_DUMP_START       "StartDump"
+#define EDG_WLL_STATE_DUMP_END         "EndDump"
+
+#ident "$Header$"
+
+int edg_wll_GetServerState(edg_wll_Context,const char *,char **);
+int edg_wll_SetServerState(edg_wll_Context,const char *,const char *);
+
+#endif
diff --git a/org.glite.lb.server/src/srv_purge.c b/org.glite.lb.server/src/srv_purge.c
new file mode 100644 (file)
index 0000000..822d9c5
--- /dev/null
@@ -0,0 +1,561 @@
+#ident "$Header$"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <assert.h>
+#include <string.h>
+#include <time.h>
+
+#include "glite/wms/jobid/cjobid.h"
+
+#include "glite/lb/trio.h"
+#include "glite/lb/context.h"
+#include "glite/lb/events_parse.h"
+#include "glite/lb/mini_http.h"
+#include "glite/lb/ulm_parse.h"
+
+#include "lb_html.h"
+#include "lb_proto.h"
+#include "store.h"
+#include "lock.h"
+#include "lbs_db.h"
+#include "query.h"
+#include "get_events.h"
+#include "glite/lb/purge.h"
+#include "glite/lb/load.h"
+#include "glite/lb/dump.h"
+#include "purge.h"
+#include "lb_xml_parse.h"
+
+
+#define DUMP_FILE_STORAGE                                      "/tmp/"
+
+#define sizofa(a) (sizeof(a)/sizeof((a)[0]))
+
+static const char* const resp_headers[] = {
+       "Cache-Control: no-cache",
+       "Accept: application/x-dglb",
+       "User-Agent: edg_wll_Server/" PROTO_VERSION "/" COMP_PROTO,
+       "Content-Type: application/x-dglb",
+       NULL
+};
+
+static int purge_one(edg_wll_Context ctx,const edg_wlc_JobId,int,int);
+
+int edg_wll_CreateTmpFileStorage(edg_wll_Context ctx, char *prefix, char **fname)
+{
+       char                    fname_buf[1024];
+       struct timeval  tv;
+       int                             retfd;
+
+
+       while ( 1 )
+       {
+               gettimeofday(&tv, NULL);
+               snprintf(fname_buf, 1024, "%s/%ld_%ld", prefix, tv.tv_sec, tv.tv_usec);
+
+               if ( (retfd = open(fname_buf, O_WRONLY|O_CREAT|O_EXCL|O_APPEND,0600)) == -1 )
+               {
+                       if ( errno != EEXIST )
+                       {
+                               char buff[100];
+
+                               sprintf(buff, "couldn't create temporary server file");
+                               return edg_wll_SetError(ctx, errno, buff);
+                       }
+               }
+               else
+                       break;
+       }
+
+       *fname = strdup(fname_buf);
+
+       return retfd;
+}
+
+int edg_wll_CreateFileStorageFromTmp(edg_wll_Context ctx, char *old_file, char *file_type, char **fname)
+{
+       char                    fname_buf[1024],
+                                       fname_prefix[1024],
+                                       stimebuf[100];
+       char               *stmp;
+       struct timeval  tv;
+       int                             retfd;
+
+
+       if ( (stmp = strrchr(old_file, '/')) )
+       {
+               strncpy(fname_prefix, old_file, stmp - old_file);
+               fname_prefix[stmp - old_file] = '\0';
+       }
+
+       while ( 1 )
+       {
+               gettimeofday(&tv, NULL);
+               edg_wll_ULMTimevalToDate(tv.tv_sec, tv.tv_usec, stimebuf);
+               snprintf(fname_buf, 1024, "%s/%s_%s", fname_prefix, file_type, stimebuf);
+
+               if ( !link(old_file, fname_buf) )
+                       break;
+
+               if ( errno != EEXIST )
+               {
+                       char buff[100];
+
+                       sprintf(buff, "couldn't create server %s file", file_type);
+                       return edg_wll_SetError(ctx, errno, buff);
+               }
+       }
+
+       *fname = strdup(fname_buf);
+
+       return retfd;
+}
+
+int edg_wll_CreateFileStorage(edg_wll_Context ctx, char *file_type, char *prefix, char **fname)
+{
+       char                    fname_buf[1024],
+                                       stimebuf[100];
+       struct timeval  tv;
+       int                             retfd;
+
+       if ( *fname )
+       {
+               snprintf(fname_buf, 1024, "%s/%s", prefix? prefix: "", *fname);
+               if ( (retfd = open(*fname, O_WRONLY|O_CREAT|O_EXCL|O_APPEND,0600)) == -1 )
+               {
+                       char buff[100];
+                       if ( errno == EEXIST )
+                               sprintf(buff, "Server file %s exist", fname_buf);
+                       else
+                               sprintf(buff, "Server couldn't create file %s", fname_buf);
+                       edg_wll_SetError(ctx, errno, buff);
+
+                       return -1;
+               }
+               if ( prefix )
+               {
+                       free(*fname);
+                       *fname = strdup(fname_buf);
+               }
+
+               return retfd;
+       }
+
+       while ( 1 )
+       {
+               gettimeofday(&tv, NULL);
+               edg_wll_ULMTimevalToDate(tv.tv_sec, tv.tv_usec, stimebuf);
+               if ( !prefix )
+               {
+                       if ( strcmp(file_type,FILE_TYPE_PURGE) == 0 )
+                               prefix = ctx->purgeStorage;
+                       else if ( strcmp(file_type,FILE_TYPE_DUMP) == 0 )
+                               prefix = ctx->dumpStorage;
+                       else
+                               prefix = "";
+               }
+               snprintf(fname_buf, 1024, "%s/%s_%s", prefix, file_type, stimebuf);
+
+               if ( (retfd = open(fname_buf, O_WRONLY|O_CREAT|O_EXCL|O_APPEND,0600)) == -1 )
+               {
+                       if ( errno != EEXIST )
+                       {
+                               char buff[100];
+
+                               sprintf(buff, "couldn't create server %s file", file_type);
+                               edg_wll_SetError(ctx, errno, buff);
+
+                               return -1;
+                       }
+               }
+               else
+                       break;
+       }
+
+       *fname = strdup(fname_buf);
+
+       return retfd;
+}
+
+int edg_wll_PurgeServer(edg_wll_Context ctx,const edg_wll_PurgeRequest *request)
+{
+       int     i,parse = 0,dumpfile = -1;
+       edg_wlc_JobId   job;
+       char    *message = NULL, *response = NULL;
+       char    *tmpfname;
+       int     naffected_jobs = 0;
+       edg_wll_PurgeResult result;
+       int             ret = HTTP_OK;
+
+
+       if (!ctx->noAuth) {
+               edg_wll_SetError(ctx,EPERM,"only superusers may purge");
+               goto abort;
+       }
+
+       edg_wll_ResetError(ctx);
+       memset(&result, 0, sizeof(edg_wll_PurgeResult));
+
+
+       if ( (request->flags & EDG_WLL_PURGE_SERVER_DUMP) && 
+                ((dumpfile = edg_wll_CreateTmpPurgeFile(ctx, &tmpfname)) == -1 ) )
+               return edg_wll_Error(ctx, NULL, NULL);
+
+       if (request->flags&EDG_WLL_PURGE_REALLY_PURGE) {
+               edg_wll_DumpRequest     req = {
+                       EDG_WLL_DUMP_LAST_END, EDG_WLL_DUMP_NOW
+               };
+               edg_wll_DumpResult      res;
+
+               if (edg_wll_DumpEvents(ctx,&req,&res)) 
+               {
+                       if ( request->flags & EDG_WLL_PURGE_SERVER_DUMP )
+                               unlink(tmpfname);
+                       return edg_wll_Error(ctx, NULL, NULL);
+               }
+       }
+
+       if (request->jobs) for (i=0; request->jobs[i]; i++) {
+               if (edg_wlc_JobIdParse(request->jobs[i],&job)) {
+                       fprintf(stderr,"%s: parse error\n",request->jobs[i]);
+                       parse = 1;
+               }
+               else {
+                       if (check_strict_jobid(ctx,job)) {
+                               fprintf(stderr,"%s: not my job\n",request->jobs[i]);
+                               parse = 1;
+                       }
+                       else {
+                               switch (purge_one(ctx,job,dumpfile,request->flags&EDG_WLL_PURGE_REALLY_PURGE)) {
+                                       case 0: if (request->flags & EDG_WLL_PURGE_LIST_JOBS) {
+                                                       result.jobs = realloc(result.jobs,(naffected_jobs+2) * sizeof(*result.jobs));
+                                                       result.jobs[naffected_jobs] = strdup(request->jobs[i]);
+                                                       result.jobs[naffected_jobs+1] = NULL;
+                                               }
+                                               naffected_jobs++;
+                                               break;
+                                       case ENOENT: parse = 1;
+                                                    edg_wll_ResetError(ctx);
+                                                    break;
+                                       default: goto abort;
+                               }
+
+                       }
+                       edg_wlc_JobIdFree(job);
+               }
+       }
+       else {
+               edg_wll_Stmt    s;
+               char            *job_s;
+               int             res;
+               time_t          timeout[EDG_WLL_NUMBER_OF_STATCODES],
+                               now = time(NULL);
+
+               for (i=0; i<EDG_WLL_NUMBER_OF_STATCODES; i++)
+                       timeout[i] = request->timeout[i] < 0 ? ctx->purge_timeout[i] : request->timeout[i];
+
+               if (edg_wll_ExecStmt(ctx,"select dg_jobid from jobs",&s) < 0) goto abort;
+               while ((res = edg_wll_FetchRow(s,&job_s)) > 0) {
+                       if (edg_wlc_JobIdParse(job_s,&job)) {
+                               fprintf(stderr,"%s: parse error (internal inconsistency !)\n",job_s);
+                               parse = 1;
+                       }
+                       else {
+                               edg_wll_JobStat stat;
+
+                               if (check_strict_jobid(ctx,job)) {
+                                       edg_wlc_JobIdFree(job);
+                                       free(job_s);
+                                       parse = 1;
+                                       continue;
+                               }
+
+                               memset(&stat,0,sizeof stat);
+                               if (edg_wll_JobStatus(ctx,job,0,&stat)) goto abort; /* XXX: memory leak */
+
+                               switch (stat.state) {
+                                       case EDG_WLL_JOB_CLEARED:
+                                       case EDG_WLL_JOB_ABORTED:
+                                       case EDG_WLL_JOB_CANCELLED:
+                                               i = stat.state;
+                                               break;
+                                       default:
+                                               i = EDG_WLL_PURGE_JOBSTAT_OTHER;
+                               }
+
+                               if (now-stat.lastUpdateTime.tv_sec > timeout[i] && !check_strict_jobid(ctx,job))
+                               {
+                                       if (purge_one(ctx,job,dumpfile,request->flags&EDG_WLL_PURGE_REALLY_PURGE))
+                                               goto abort;
+
+                               /* XXX: change with the streaming interface */
+                                       if (request->flags & EDG_WLL_PURGE_LIST_JOBS) {
+                                               result.jobs = realloc(result.jobs,(naffected_jobs+2) * sizeof(*result.jobs));
+                                               result.jobs[naffected_jobs] = job_s;
+                                               result.jobs[naffected_jobs+1] = NULL;
+                                               job_s = NULL;
+                                       }
+                                       naffected_jobs++;
+                               }
+
+                               edg_wlc_JobIdFree(job);
+                               edg_wll_FreeStatus(&stat);
+                               free(job_s);
+                       }
+               }
+               edg_wll_FreeStmt(&s);
+abort:
+                // just for escaping from nested cycles
+               ;       /* prevent compiler to complain */
+       }
+
+       if (parse && !edg_wll_Error(ctx,NULL,NULL))
+       {
+               if ( naffected_jobs )
+                       edg_wll_SetError(ctx,EINVAL,"Invalid JobId(s) but other jobs purged");
+               else
+                       edg_wll_SetError(ctx,EINVAL,"Invalid JobId(s)");
+       }
+
+       switch ( edg_wll_Error(ctx,NULL,NULL) )
+       {
+               case 0:
+                       ret = HTTP_OK;
+                       break;
+               case EINVAL:
+                       ret = HTTP_INVALID;
+                       break;
+               case EPERM:
+                       ret = HTTP_UNAUTH;
+                       break;
+               
+               /* fatal errors */
+               case ENOMEM:
+                       /* fall through */
+               default:
+                       ret = HTTP_INTERNAL;
+                       break;
+       }
+       
+       if (ret != HTTP_INTERNAL) {
+               if ( request->flags & EDG_WLL_PURGE_SERVER_DUMP )
+               {
+                       edg_wll_CreatePurgeFileFromTmp(ctx, tmpfname, &(result.server_file));
+                       unlink(tmpfname);
+               }
+
+               if ( edg_wll_PurgeResultToXML(ctx, &result, &message) )
+                       ret = HTTP_INTERNAL;
+               else 
+                       printf("%s", message);
+       }
+       
+       if ( result.server_file )
+               free(result.server_file);
+       if ( result.jobs )
+       {
+               for ( i = 0; result.jobs[i]; i++ )
+                       free(result.jobs[i]);
+               free(result.jobs);
+       }
+
+       asprintf(&response, "HTTP/1.1 %d %s", ret, edg_wll_HTTPErrorMessage(ret));
+
+       edg_wll_http_send(ctx, response, resp_headers, message);
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static void unlock_and_check(edg_wll_Context ctx,edg_wlc_JobId job)
+{
+       char    *job_s,*et,*ed;
+
+       if (edg_wll_UnlockJob(ctx,job)) {
+               job_s = edg_wlc_JobIdUnparse(job);
+
+               edg_wll_Error(ctx,&et,&ed);
+               fprintf(stderr,"%s: edg_wll_UnlockJob(): %s (%s) -- expect bogus things\n",
+                               job_s,et,ed);
+               syslog(LOG_CRIT,"%s: edg_wll_UnlockJob(): %s (%s) -- expect bogus things",
+                               job_s,et,ed);
+               free(et); free(ed); free(job_s);
+       }
+}
+
+
+int purge_one(edg_wll_Context ctx,const edg_wlc_JobId job,int dump, int purge)
+{
+       char    *dbjob;
+       char    *stmt = NULL;
+       edg_wll_Stmt    q;
+       int             ret,dumped = 0;
+
+       edg_wll_ResetError(ctx);
+       if ( !purge && dump < 0 ) return 0;
+
+       dbjob = edg_wlc_JobIdGetUnique(job);    /* XXX: strict jobid already checked */
+       if (edg_wll_LockJob(ctx,job)) goto clean;
+
+       if ( purge )
+       {
+               trio_asprintf(&stmt,"delete from jobs where jobid = '%|Ss'",dbjob);
+               ret = edg_wll_ExecStmt(ctx,stmt,NULL);
+               if (ret <= 0) {
+                       unlock_and_check(ctx,job);
+                       if (ret == 0) {
+                               fprintf(stderr,"%s: no such job\n",dbjob);
+                               edg_wll_SetError(ctx,ENOENT,dbjob);
+                       }
+                       goto clean;
+               }
+               free(stmt); stmt = NULL;
+
+               trio_asprintf(&stmt,"delete from states where jobid = '%|Ss'",dbjob);
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) {
+                       unlock_and_check(ctx,job);
+                       goto clean;
+               }
+               free(stmt); stmt = NULL;
+
+/* Why on earth ?
+               trio_asprintf(&stmt,"delete from states where jobid = '%|Ss'",dbjob);
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) {
+                       unlock_and_check(ctx,job);
+                       goto clean;
+               }
+               free(stmt); stmt = NULL;
+*/
+
+       }
+
+       if (!ctx->strict_locking) unlock_and_check(ctx,job);
+
+       if ( purge )
+       {
+               trio_asprintf(&stmt,"delete from status_tags where jobid = '%|Ss'",dbjob);
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) goto unlock;
+               free(stmt); stmt = NULL;
+       }
+
+       if (dump >= 0) 
+               trio_asprintf(&stmt,
+                       "select event,code,prog,host,u.cert_subj,time_stamp,usec,level,arrived "
+                       "from events e,users u "
+                       "where e.jobid='%|Ss' "
+                       "and u.userid=e.userid "
+                       "order by event", dbjob);
+       else
+               trio_asprintf(&stmt,"select event from events "
+                       "where jobid='%|Ss' "
+                       "order by event", dbjob);
+
+/* check for events repeatedly -- new one may have arrived in the meantime */
+       while ((ret = edg_wll_ExecStmt(ctx,stmt,&q)) > 0) {
+               char    *res[9];
+
+               dumped = 1;
+               while ((ret = edg_wll_FetchRow(q,res)) > 0) {
+                       int     event;
+
+                       event = atoi(res[0]);
+                       free(res[0]); res[0] = NULL;
+
+                       if (dump >= 0) {
+                               edg_wll_Event   e;
+
+                               assert(ret == 9);
+                               res[0] = edg_wlc_JobIdUnparse(job);
+                               if (convert_event_head(ctx,res,&e) || edg_wll_get_event_flesh(ctx,event,&e))
+                               {
+                                       char    *et,*ed;
+                                       int     i;
+
+                               /* Most likely sort of internal inconsistency. 
+                                * Must not be fatal -- just complain
+                                */
+                                       edg_wll_Error(ctx,&et,&ed);
+                                       fprintf(stderr,"%s event %d: %s (%s)\n",dbjob,event,et,ed);
+                                       syslog(LOG_WARNING,"%s event %d: %s (%s)",dbjob,event,et,ed);
+                                       free(et); free(ed);
+                                       for (i=0; i<sizofa(res); i++) free(res[i]);
+                                       edg_wll_ResetError(ctx);
+                               }
+                               else {
+                                       char    *event_s = edg_wll_UnparseEvent(ctx,&e);
+                                       char    arr_s[100];
+                                       int     len, written, total;
+
+                                       strcpy(arr_s, "DG.ARRIVED=");
+                                       edg_wll_ULMTimevalToDate(e.any.arrived.tv_sec,
+                                                                       e.any.arrived.tv_usec,
+                                                                       arr_s+strlen("DG.ARRIVED="));
+
+                                       len = strlen(arr_s);
+                                       total = 0;
+                                       while (total != len) {
+                                               written = write(dump,arr_s+total,len-total);
+                                               if (written < 0 && errno != EAGAIN) {
+                                                       edg_wll_SetError(ctx,errno,"writing dump file");
+                                                       free(event_s);
+                                                       goto clean;
+                                               }
+                                               total += written;
+                                       }
+                                       write(dump, " ", 1);
+                                       
+                                       len = strlen(event_s);
+                                       total = 0;
+                                       while (total != len) {
+                                               written = write(dump,event_s+total,len-total);
+                                               if (written < 0 && errno != EAGAIN) {
+                                                       perror("dump to file");
+                                                       syslog(LOG_ERR,"dump to file: %m");
+                                                       dump = -1; /* XXX: likely to be a permanent error
+                                                                   * give up writing but do purge */
+                                                       break;
+                                               }
+                                               total += written;
+                                       }
+                                       /* write(dump,"\n",1); edg_wll_UnparseEvent does so */
+                                       free(event_s);
+                               }
+                               edg_wll_FreeEvent(&e);
+                       }
+
+                       if ( purge ) {
+                               if (edg_wll_delete_event(ctx,dbjob,event)) {
+                                       char    *et,*ed;
+
+                               /* XXX: just complain and carry on. Is it OK? */
+                                       edg_wll_Error(ctx,&et,&ed);
+                                       fprintf(stderr,"%s event %d: %s (%s)\n",dbjob,event,et,ed);
+                                       syslog(LOG_WARNING,"%s event %d: %s (%s)",dbjob,event,et,ed);
+                                       free(et); free(ed);
+                                       edg_wll_ResetError(ctx);
+                               }
+                       }
+               }
+               edg_wll_FreeStmt(&q);
+               if (ret < 0 || !purge) break;
+       }
+
+       edg_wll_FreeStmt(&q);
+       if (ret == 0 && dumped == 0) {
+               if (ctx->strict_locking) unlock_and_check(ctx,job);
+               fprintf(stderr,"%s: no events, i.e. no such job or internal inconsistency\n",dbjob);
+               edg_wll_SetError(ctx,ENOENT,dbjob);
+               goto clean;
+       }
+
+unlock:
+       if (ctx->strict_locking) unlock_and_check(ctx,job);
+
+clean:
+       free(dbjob);
+       free(stmt);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.server/src/store.c.T b/org.glite.lb.server/src/store.c.T
new file mode 100644 (file)
index 0000000..c71e33b
--- /dev/null
@@ -0,0 +1,566 @@
+#ident "$Header$"
+
+/*
+
+@@@AUTO
+
+ * XXX: still lots of hardcoded stuff
+ *     there's mapping db.column <-> event struct field
+ */
+
+@@@LANG: C
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include <globus_config.h>
+
+#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h"
+#include "glite/wms/jobid/strmd5.h"
+
+#include "glite/lb/events_parse.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/log_proto.h"        /* for EDG_WLL_LOG_USER_DEFAULT */
+#include "glite/lb/trio.h"
+
+#include "store.h"
+#include "get_events.h"
+#include "lbs_db.h"
+#include "lock.h"
+#include "lb_authz.h"
+
+static int store_user(edg_wll_Context,const char *,const char *); 
+static int store_job(edg_wll_Context,const edg_wlc_JobId,const char *);
+static int store_flesh(edg_wll_Context,edg_wll_Event *,char *,int);
+static int store_seq(edg_wll_Context,edg_wll_Event *,int);
+static int check_dup(edg_wll_Context,edg_wll_Event *);
+static int check_auth(edg_wll_Context,edg_wll_Event *e); 
+static int register_subjobs(edg_wll_Context,const edg_wll_RegJobEvent *);
+
+void edg_wll_StoreAnonymous(edg_wll_Context ctx,int anon) {
+       ctx->allowAnonymous = anon;
+}
+
+int edg_wll_StoreEvent(edg_wll_Context ctx,edg_wll_Event *e,int *seq)
+{
+       edg_wll_ErrorCode       err = 0;
+       char            *userid = NULL,*jobid,*stmt;
+       char            *select_max,*ssrc;
+       edg_wll_Stmt    sh = NULL;
+       int             next = 0xDEAD;
+       char            *now_s = NULL;
+
+       ssrc = jobid = stmt = select_max = NULL;
+
+       if ( ctx->event_load )
+               now_s = strdup(edg_wll_TimeToDB(e->any.arrived.tv_sec));
+       else
+       now_s = strdup(edg_wll_TimeToDB(time(NULL)));
+       edg_wll_ResetError(ctx);
+       switch (err = check_auth(ctx,e)) {
+               case 0: break;
+               case ENOENT: goto clean;
+               case EPERM:
+                       if (!ctx->noAuth) goto clean;
+                       edg_wll_ResetError(ctx);
+                       break;
+               default: goto clean;
+       }
+       if ((err = check_dup(ctx,e))) goto clean;
+
+       userid = strdup(strmd5(e->any.user,NULL));
+
+/* make sure user record is there */
+       if ((err = store_user(ctx,userid,e->any.user))) goto clean;
+
+       jobid = edg_wlc_JobIdGetUnique(e->any.jobId);
+
+/* only REGJOB events determine job owner now */
+       if (e->type == EDG_WLL_EVENT_REGJOB && 
+               (err = store_job(ctx,e->any.jobId,userid))) goto clean;
+
+
+/* obtain next event sequence number */
+       trio_asprintf(&select_max,
+               "select max(event) from events "
+               "where jobid = '%|Ss'",jobid);
+
+       ssrc = edg_wll_SourceToString(e->any.source);
+
+/* try to insert (someone else may be doing the same) */
+       while (1) {
+               char    *max;
+
+               if (edg_wll_ExecStmt(ctx,select_max,&sh) < 0 ||
+                   edg_wll_FetchRow(sh,&max) < 0)
+               {
+                       err = edg_wll_Error(ctx,NULL,NULL);
+                       goto clean;
+               }
+               edg_wll_FreeStmt(&sh); 
+               
+               next = max && *max ? atoi(max)+1 : 0;
+               
+       /* store an UNDEF event first in order to prevent race condition
+        * with readers */
+               trio_asprintf(&stmt,
+                       "insert into events(jobid,event,code,prog,host,time_stamp,usec,arrived,level,userid) "
+                       "values ('%|Ss',%d,%d,'%|Ss','%|Ss',%s,%d,'%|Ss',%d,'%|Ss')",
+                       jobid,next,EDG_WLL_EVENT_UNDEF,ssrc,e->any.host,
+                       edg_wll_TimeToDB(e->any.timestamp.tv_sec),e->any.timestamp.tv_usec,
+                       now_s, e->any.level,userid);
+
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) {
+                       if ((err = edg_wll_Error(ctx,NULL,NULL)) != EEXIST)
+                               goto clean;
+               } else break; /* successful insert */
+
+       /* we were late -- try once again */
+               free(stmt);
+       }
+
+       free(stmt);
+       if ((err = store_seq(ctx,e,next)) ||
+               (err = store_flesh(ctx,e,jobid,next))) {
+       /* attempt to cleanup, ignore new errors */
+               char            *desc;
+               edg_wll_ErrorCode       oerr = edg_wll_Error(ctx,NULL,&desc);
+
+               edg_wll_delete_event(ctx,jobid,next);
+               edg_wll_SetError(ctx,oerr,desc);
+               free(desc);
+       }
+       else {
+       /* emulate commit, i.e. swith to a real event type to make
+        * the record valid */
+               trio_asprintf(&stmt,
+                       "update events set code=%d "
+                       "where jobid='%|Ss' and event=%d",
+                       (int) e->any.type,jobid,next);
+               switch (edg_wll_ExecStmt(ctx,stmt,NULL)) {
+                       case 0: if (ctx->strict_locking)
+                                       err = edg_wll_SetError(ctx,ENOENT,"event disappeared on store while strict locking");
+                       /* purge in progres: drop the garbage, ignore errors */
+                               else {
+                                       edg_wll_delete_event(ctx,jobid,next);
+                                       err = edg_wll_SetError(ctx,ENOENT,"job being purged");
+                               }
+                               break;
+                       case 1: if (ctx->strict_locking) err = 0;
+                               else {
+                       /* check whether the job is still there to prevent garbage
+                        * left while there is a concurrent purge 
+                        */
+                                       free(stmt);
+                                       trio_asprintf(&stmt,
+                                               "select 'x' from jobs where jobid='%|Ss'",
+                                               jobid);
+                                       switch (edg_wll_ExecStmt(ctx,stmt,NULL)) {
+                                               case 1: break;
+                                               case 0: /* purge in progres */
+                                                       edg_wll_delete_event(ctx,jobid,next);
+                                                       err = edg_wll_SetError(ctx,ENOENT,"job being purged");
+                                                       break;
+                                               default: err = edg_wll_SetError(ctx,EDG_WLL_ERROR_DB_CALL,
+                                                               "more job records, what is that?");
+                                                       break;
+                                       }
+                               }
+                               break;
+                       case -1: err = edg_wll_Error(ctx,NULL,NULL);
+                               break;
+
+                       default: err = edg_wll_SetError(ctx,EDG_WLL_ERROR_DB_CALL,
+                               "more event records, what is that?");
+                               break;
+               }
+       }
+
+       if (err == 0 && 
+               e->any.type == EDG_WLL_EVENT_REGJOB &&
+               (e->regJob.jobtype == EDG_WLL_REGJOB_DAG ||
+                e->regJob.jobtype == EDG_WLL_REGJOB_PARTITIONED) &&
+               e->regJob.nsubjobs > 0) err = register_subjobs(ctx,&e->regJob);
+
+
+clean:
+       free(now_s);
+       free(userid);
+       free(jobid);
+       free(stmt);
+       free(ssrc);
+       free(select_max);
+       if (sh) edg_wll_FreeStmt(&sh);
+       if (!err && seq) *seq = next;
+       return err;
+}
+
+static int store_user(edg_wll_Context ctx,const char *userid,const char *subj)
+{
+       char    *stmt;
+
+       trio_asprintf(&stmt,"insert into users(userid,cert_subj) "
+               "values ('%|Ss','%|Ss')",userid,subj);
+
+       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) {
+               if (edg_wll_Error(ctx,NULL,NULL) == EEXIST)
+                       edg_wll_ResetError(ctx);
+       }
+
+       free(stmt);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static int store_job(edg_wll_Context ctx,const edg_wlc_JobId job,const char *userid)
+{
+       char *jobstr = edg_wlc_JobIdUnparse(job);
+       char *jobid = edg_wlc_JobIdGetUnique(job);
+       char *stmt;
+
+/* debug Duplicate key on index: Duplicate entry '(nil)' for key 1
+ */
+       if (jobid == NULL || jobstr == NULL) 
+               return edg_wll_SetError(ctx,EINVAL,"store_job()");
+
+       edg_wll_ResetError(ctx);
+       trio_asprintf(&stmt,"insert into jobs(jobid,dg_jobid,userid) "
+               "values ('%|Ss','%|Ss','%|Ss')",jobid,jobstr,userid);
+
+       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) {
+               if (edg_wll_Error(ctx,NULL,NULL) == EEXIST) 
+                       edg_wll_ResetError(ctx);
+       }
+       free(stmt);
+       free(jobstr);
+       free(jobid);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+/*
+ * XXX: store it in SHORT_FIELDS for now despite it should go to dedicated
+ *     column in EVENTS.
+ *
+ *     don't want to change the database structure now, will be done anyway
+ *     soon
+ */
+static int store_seq(edg_wll_Context ctx,edg_wll_Event *e,int no)
+{
+       int     ret;
+       char    *stmt;
+       char    *jobid = edg_wlc_JobIdGetUnique(e->any.jobId);
+
+       edg_wll_ResetError(ctx);
+       trio_asprintf(&stmt,"insert into short_fields(jobid,event,name,value) "
+                       "values ('%|Ss',%d,'SEQCODE','%|Ss')",
+                       jobid,no,e->any.seqcode);
+
+       ret = edg_wll_ExecStmt(ctx,stmt,NULL);
+       free(stmt);
+       free(jobid);
+
+       return ret>=0 ? 0 : edg_wll_Error(ctx,NULL,NULL);
+}
+
+#define SHORT_LEN      255     /* short_fiels.value db column lenght */
+
+static int store_flesh(edg_wll_Context ctx,edg_wll_Event *e,char *jobid,int no)
+{
+       struct {
+               char    *key;
+               char    *val;
+       } f[20];
+
+       char    *stmt;
+       unsigned int    i;
+       int     err = 0;
+
+       edg_wll_ResetError(ctx);
+       memset(f,0,sizeof(f)); assert(f[0].key == NULL);
+
+       switch (e->type) {
+@@@{
+       for my $type (getTypesOrdered $event) {
+               next if $type eq '_common_';
+               selectType $event $type;
+               my $uctype = uc $type;
+               my $flctype = lcfirst $type;
+               gen qq{
+!              case EDG_WLL_EVENT_$uctype:
+};
+               my $idx = 0;
+               for (getFieldsOrdered $event) {
+                       my $f = selectField $event $_;
+                       my $name = getName $f;
+                       my $ucname = uc $name;
+                       my $fucname = ucfirst $name;
+                       my $tos = $f->{codes} ?
+                               "f[$idx].val = edg_wll\_$type${fucname}ToString(e->$flctype.$name);" :
+                               toString $f "e->$flctype.$name","f[$idx].val";
+                       gen qq{
+!                      f[$idx].key = "$ucname";
+!                      $tos
+};
+                       $idx++;
+               }
+               gen qq{
+!                      assert($idx<sizeof f/sizeof f[0]);
+!                      break;
+};
+       }
+@@@}
+               default:
+                       break;
+       }
+
+       for (i=0; i<sizeof(f)/sizeof(f[0]) && !err; i++) if (f[i].key && f[i].val) {
+               trio_asprintf(&stmt,"insert into %s(jobid,event,name,value) "
+                       "values ('%|Ss',%d,'%|Ss','%|Ss')",
+                       strlen(f[i].val) <= SHORT_LEN ? "short_fields" : "long_fields",
+                       jobid,no,f[i].key,f[i].val);
+
+               if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) err = edg_wll_Error(ctx,NULL,NULL);
+               free(stmt);
+       }
+       for (i=0; i<sizeof(f)/sizeof(f[0]); i++) free(f[i].val);
+
+/* XXX: hardcoded, no other suitable place to store it */
+       trio_asprintf(&stmt,"insert into short_fields(jobid,event,name,value) "
+               "values ('%|Ss',%d,'SRC_INSTANCE','%|Ss')",
+               jobid,no,e->any.src_instance);
+       if (edg_wll_ExecStmt(ctx,stmt,NULL) < 0) err = edg_wll_Error(ctx,NULL,NULL);
+       free(stmt);
+
+       return err;
+}
+
+static int check_dup(edg_wll_Context ctx,edg_wll_Event *e)
+{
+       int             i,dup_detected = 0;
+       int             err;
+       char            *es,*es2;
+       edg_wll_QueryRec        jc[2],ec[2];
+       edg_wll_QueryRec        **jca, **eca;
+       edg_wll_Event   *e2;
+
+       edg_wll_ResetError(ctx);
+
+       jc[0].attr = EDG_WLL_QUERY_ATTR_JOBID;
+       jc[0].value.j = e->any.jobId;
+       jc[0].op = EDG_WLL_QUERY_OP_EQUAL;
+       jc[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+       ec[0].attr = EDG_WLL_QUERY_ATTR_TIME;
+       memcpy(&ec[0].value.t,&e->any.timestamp,sizeof(struct timeval));
+       ec[0].op = EDG_WLL_QUERY_OP_EQUAL;
+       ec[1].attr = EDG_WLL_QUERY_ATTR_UNDEF;
+
+        jca = (edg_wll_QueryRec **) malloc (2 * sizeof(edg_wll_QueryRec **));
+        eca = (edg_wll_QueryRec **) malloc (2 * sizeof(edg_wll_QueryRec **));
+        jca[0] = jc;
+        jca[1] = NULL;
+        eca[0] = ec;
+        eca[1] = NULL;
+
+       err = edg_wll_QueryEventsServer(ctx,1,(const edg_wll_QueryRec **)jca, 
+                                       (const edg_wll_QueryRec **)eca,&e2);
+       switch (err) {
+               case 0: /* continue normally */
+                       break;
+               case ENOENT:
+                       free(jca);
+                       free(eca);
+                       return edg_wll_ResetError(ctx);
+                       break;
+               default:
+                       free(jca);
+                       free(eca);
+                       return edg_wll_Error(ctx,NULL,NULL);
+                       break;
+       }
+
+       es = edg_wll_UnparseEvent(ctx,e);
+       assert(es);
+
+       for (i=0;e2[i].type && !dup_detected;i++) {
+               /* Ignore priority */
+               e2[i].any.priority = e->any.priority;
+               es2 = edg_wll_UnparseEvent(ctx,e2+i);
+               assert(es2);
+               if (!strcmp(es,es2)) {
+                       dup_detected = 1;
+                       edg_wll_SetError(ctx,EEXIST,"duplicate event");
+               }
+               free(es2);
+       }
+
+       free(jca);
+       free(eca);
+       free(es);
+       for (i=0; e2[i].type; i++) edg_wll_FreeEvent(e2+i);
+       free(e2);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static int check_auth(edg_wll_Context ctx,edg_wll_Event *e)
+{
+       char    *jobid = edg_wlc_JobIdGetUnique(e->any.jobId);
+       char    *q = NULL,*owner = NULL;
+       edg_wll_Stmt    stmt = NULL;
+       char    *user;
+
+       edg_wll_ResetError(ctx);
+
+       if (e->type == EDG_WLL_EVENT_REGJOB) 
+               return strcmp(e->any.user,EDG_WLL_LOG_USER_DEFAULT) ?
+                       0 : edg_wll_SetError(ctx,EPERM,"can't register jobs anonymously");
+
+       trio_asprintf(&q,"select userid from jobs where jobid='%|Ss'",jobid);
+
+       if (edg_wll_ExecStmt(ctx,q,&stmt) < 0
+               || edg_wll_FetchRow(stmt,&owner) < 0
+       ) goto clean;
+
+       if (!owner) {
+               edg_wll_SetError(ctx,ENOENT,"job not registered");
+               goto clean;
+       }
+
+       switch (e->any.source) {
+               case EDG_WLL_SOURCE_USER_INTERFACE:
+               case EDG_WLL_SOURCE_LRMS:
+               case EDG_WLL_SOURCE_APPLICATION:
+                       user = strmd5(e->any.user,NULL);
+                       if (strcmp(owner,user)) edg_wll_SetError(ctx,EPERM,"check_auth()");
+                       break;
+               default:
+                       /* XXX: just don't allow anonymous */
+                       if (!strcmp(e->any.user,EDG_WLL_LOG_USER_DEFAULT))
+                               edg_wll_SetError(ctx,EPERM,"check_auth()");
+                       break;
+       }
+
+
+clean:
+       if (stmt) edg_wll_FreeStmt(&stmt);
+       free(q);
+       free(owner);
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+static int register_subjobs(edg_wll_Context ctx,const edg_wll_RegJobEvent *e)
+{
+       int     i,err;
+       edg_wlc_JobId   *subjobs;
+       struct timeval  now;
+
+       edg_wll_ResetError(ctx);
+       if (e->nsubjobs == 0) return 0;
+       if (e->nsubjobs < 0) return edg_wll_SetError(ctx,EINVAL,"negative number of subjobs");
+
+       if ((err = edg_wll_GenerateSubjobIds(ctx,e->jobId,e->nsubjobs,e->seed,&subjobs)))
+               return err;
+
+       gettimeofday(&now,NULL);
+
+       for (i=0; i<e->nsubjobs; i++) {
+               edg_wll_Event   e2;
+               int             seq;
+               char            *et,*ed,*job_s;
+
+               memset(&e2,0,sizeof e2);
+               e2.type = EDG_WLL_EVENT_REGJOB;
+               e2.any.jobId = subjobs[i]; subjobs[i] = NULL;
+               memcpy(&e2.regJob.timestamp,&now,sizeof now);
+               e2.any.host = strdup(ctx->srvName);
+               e2.any.level = e->level;
+               e2.any.priority = e->priority;
+               e2.any.seqcode = NULL;          /* XXX: I'm not sure :-( */
+               e2.any.user = strdup(e->user);
+               e2.any.source = e->source;
+               e2.regJob.ns = strdup(e->ns);
+               edg_wlc_JobIdDup(e->jobId,&e2.regJob.parent);
+               e2.regJob.jobtype = EDG_WLL_REGJOB_SIMPLE;
+               e2.regJob.jdl = strdup("");
+
+               switch (edg_wll_StoreEvent(ctx,&e2,&seq)) {
+
+                       case 0: break;
+                       /* maybe some non-ignorable errors should be handled here */
+
+                       default: 
+                               edg_wll_Error(ctx,&et,&ed);
+                               job_s = edg_wlc_JobIdUnparse(e2.any.jobId);
+                               fprintf(stderr,"register subjob %s: %s (%s)\n",job_s,et,ed);
+                               syslog(LOG_ERR,"register subjob %s: %s (%s)",job_s,et,ed);
+                               free(job_s); free(et); free(ed);
+                               edg_wll_FreeEvent(&e2);
+                               edg_wll_ResetError(ctx);
+                               continue;
+               }
+
+               if (edg_wll_LockJob(ctx,e2.any.jobId)) {
+                       job_s = edg_wlc_JobIdUnparse(e2.any.jobId);
+                       fprintf(stderr,"lock job %s: %s (%s)\n",job_s,et,ed);
+                       syslog(LOG_ERR,"lock job %s: %s (%s)",job_s,et,ed);
+                       free(job_s); free(et); free(ed);
+                       edg_wll_FreeEvent(&e2);
+                       edg_wll_ResetError(ctx);
+                       continue;
+               }
+
+               if ((err = edg_wll_StepIntState(ctx,e2.any.jobId,&e2,seq,NULL)))
+                       edg_wll_Error(ctx,&et,&ed);
+
+               edg_wll_UnlockJob(ctx,e2.any.jobId);
+               edg_wll_ResetError(ctx);
+
+               if (err) {
+                       job_s = edg_wlc_JobIdUnparse(e2.any.jobId);
+                       fprintf(stderr,"%s: %s (%s)\n",job_s,et,ed);
+                       syslog(LOG_ERR,"%s: %s (%s)",job_s,et,ed);
+                       free(job_s); free(et); free(ed);
+                       edg_wll_ResetError(ctx);
+               }
+
+               edg_wll_FreeEvent(&e2);
+       }
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
+
+int edg_wll_delete_event(edg_wll_Context ctx,const char *jobid,int event)
+{
+       char    *stmt;
+
+/* The order of tables is important to prevent another process calling
+ * StoreEvent() to get our event number and mess up the fields together.
+ *
+ * XXX: best effort: more or less ignore errors
+ *
+ */
+
+       trio_asprintf(&stmt,
+               "delete from short_fields where jobid='%|Ss' and event=%d",
+               jobid,event);
+       edg_wll_ExecStmt(ctx,stmt,NULL);
+       free(stmt);
+
+       trio_asprintf(&stmt,
+               "delete from long_fields where jobid='%|Ss' and event=%d",
+               jobid,event);
+       edg_wll_ExecStmt(ctx,stmt,NULL);
+       free(stmt);
+
+       trio_asprintf(&stmt,
+               "delete from events where jobid='%|Ss' and event=%d",
+               jobid,event);
+       edg_wll_ExecStmt(ctx,stmt,NULL);
+       free (stmt);
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.server/src/store.h b/org.glite.lb.server/src/store.h
new file mode 100644 (file)
index 0000000..ff62047
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef _LBS_STORE_H
+#define _LBS_STORE_H
+
+#ident "$Header$"
+
+#include "glite/lb/consumer.h"
+#include "lb_authz.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* store an event into the LB database */
+
+int edg_wll_StoreEvent(
+       edg_wll_Context,        /* INOUT */
+       edg_wll_Event *,        /* IN */
+       int *
+);
+
+void edg_wll_StoreAnonymous(
+       edg_wll_Context,    /* INOUT */
+       int             /* IN (boolean) */
+);
+
+/* update stored job state according to new event */
+
+edg_wll_ErrorCode edg_wll_StepIntState(
+       edg_wll_Context,        /* INOUT */
+       edg_wlc_JobId,          /* IN */
+       edg_wll_Event *,        /* IN */
+       int,                    /* IN */
+       edg_wll_JobStat *
+);
+
+int db_store(edg_wll_Context,char *,char *);
+int handle_request(edg_wll_Context,char *, int);
+int create_reply(const edg_wll_Context,char *,int);
+
+int edg_wll_delete_event(edg_wll_Context,const char *, int);
+
+
+#define USER_UNKNOWN   "unknown"
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/org.glite.lb.server/src/stored_master.c b/org.glite.lb.server/src/stored_master.c
new file mode 100644 (file)
index 0000000..87e1b3d
--- /dev/null
@@ -0,0 +1,58 @@
+#ident "$Header$"
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "glite/lb/il_string.h"
+#include "glite/lb/dgssl.h"
+#include "glite/lb/context-int.h"
+
+#include "store.h"
+
+int edg_wll_StoreProto(edg_wll_Context ctx)
+{
+       char    fbuf[256],*buf;
+       int     len,ret;
+       size_t  total;
+
+       edg_wll_ResetError(ctx);
+       if ((ret=edg_wll_ssl_read_full(ctx->connPool[ctx->connToUse].ssl,fbuf,17,&ctx->p_tmp_timeout,&total)) < 0) switch (ret) {
+               case EDG_WLL_SSL_ERROR_TIMEOUT: return edg_wll_SetError(ctx,ETIMEDOUT,"read message header");
+               case EDG_WLL_SSL_ERROR_EOF: return edg_wll_SetError(ctx,ENOTCONN,NULL);
+               default: return edg_wll_SetError(ctx,EDG_WLL_ERROR_SSL,"read message header");
+       }
+
+       if ((len = atoi(fbuf)) <= 0) return edg_wll_SetError(ctx,EINVAL,"message length");
+
+       buf = malloc(len+1);
+
+       if ((ret=edg_wll_ssl_read_full(ctx->connPool[ctx->connToUse].ssl,buf,len,&ctx->p_tmp_timeout,&total)) < 0) {
+               free(buf);
+               return edg_wll_SetError(ctx,
+                       ret == EDG_WLL_SSL_ERROR_TIMEOUT ?
+                               ETIMEDOUT : EDG_WLL_ERROR_SSL,
+                       "read message");
+       }
+
+
+       buf[len] = 0;
+
+       handle_request(ctx,buf,len);
+       free(buf);
+
+       if ((len = create_reply(ctx,fbuf,sizeof fbuf))) {
+               if ((ret = edg_wll_ssl_write_full(ctx->connPool[ctx->connToUse].ssl,fbuf,len,&ctx->p_tmp_timeout,&total)) < 0)
+                       edg_wll_SetError(ctx,
+                               ret == EDG_WLL_SSL_ERROR_TIMEOUT ? 
+                                       ETIMEDOUT : EDG_WLL_ERROR_SSL,
+                               "write reply");
+       }
+       else edg_wll_SetError(ctx,E2BIG,"create_reply()");
+
+       return edg_wll_Error(ctx,NULL,NULL);
+}
diff --git a/org.glite.lb.server/src/userjobs.c b/org.glite.lb.server/src/userjobs.c
new file mode 100644 (file)
index 0000000..a3b2ca4
--- /dev/null
@@ -0,0 +1,77 @@
+#ident "$Header$"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "glite/wms/jobid/cjobid.h"
+#include "glite/wms/jobid/strmd5.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/trio.h"
+
+#include "lbs_db.h"
+
+int edg_wll_UserJobs(
+       edg_wll_Context ctx,
+       edg_wlc_JobId   **jobs,
+       edg_wll_JobStat **states)
+{
+       char    *userid = strmd5(ctx->peerName,NULL),*stmt = NULL,
+               *res = NULL;
+       int     njobs = 0,ret,i;
+       edg_wlc_JobId   *out = NULL;
+       edg_wll_Stmt    sth = NULL;
+       edg_wll_ErrorCode       err = 0;
+
+       edg_wll_ResetError(ctx);
+
+       trio_asprintf(&stmt,"select cert_subj from users where userid = '%|Ss'",userid);
+
+       switch (edg_wll_ExecStmt(ctx,stmt,&sth)) {
+               case 0: edg_wll_SetError(ctx,ENOENT,ctx->peerName);
+               case -1: goto err;
+               default:
+                       if (edg_wll_FetchRow(sth,&res) < 0) goto err;
+                       if (strcmp(ctx->peerName,res)) {
+                               edg_wll_SetError(ctx,EDG_WLL_ERROR_MD5_CLASH,ctx->peerName);
+                               goto err;
+                       }
+       }
+
+       edg_wll_FreeStmt(&sth);
+       free(stmt); stmt = NULL;
+       free(res); res = NULL;
+
+       trio_asprintf(&stmt,"select dg_jobid from jobs where userid = '%|Ss'",userid);
+       switch (njobs = edg_wll_ExecStmt(ctx,stmt,&sth)) {
+               case 0: edg_wll_SetError(ctx,ENOENT,ctx->peerName);
+               case -1: goto err;
+       }
+
+       out = malloc(sizeof(*out)*(njobs+1));
+       memset(out,0,sizeof(*out)*(njobs+1));
+       
+       for (i=0; (ret = edg_wll_FetchRow(sth,&res)); i++) {
+               if (ret < 0) goto err;
+               if ((ret = edg_wlc_JobIdParse(res,out+i))) {
+                       edg_wll_SetError(ctx,errno,res);
+                       goto err;
+               }
+               free(res); res = NULL;
+       }
+
+err:
+       free(res);
+       free(stmt);
+       edg_wll_FreeStmt(&sth);
+       if ((err = edg_wll_Error(ctx,NULL,NULL))) {
+               if (out) {
+                   for (i=0; i<njobs; i++)
+                       edg_wlc_JobIdFree(out[i]);
+                   free(out);
+               }
+       } else *jobs = out;
+
+       return err;
+}
diff --git a/org.glite.lb.server/src/write2rgma.c b/org.glite.lb.server/src/write2rgma.c
new file mode 100755 (executable)
index 0000000..2d291fb
--- /dev/null
@@ -0,0 +1,110 @@
+#ident "$Header$"
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+
+
+#include "glite/lb/trio.h"
+#include "glite/lb/producer.h"
+#include "glite/lb/context-int.h"
+#include "glite/lb/jobstat.h"
+
+#include "get_events.h"
+#include "store.h"
+#include "lock.h"
+#include "index.h"
+#include "jobstat.h"
+
+static int     rgma_fd = -1;
+static int     rgma_sock = -1;
+static struct sockaddr_un rgma_saddr;
+
+static void write2rgma_sql(char *sqlstat)
+{
+       
+       char *rgma_fname;
+       int slen;
+       int sysret;
+       struct iovec iov[2];
+
+       assert(sqlstat != NULL);
+
+       if (rgma_fd == -1) {
+               rgma_fname = getenv("EDG_WL_RGMA_FILE");
+               if (rgma_fname == NULL) return;
+
+               rgma_fd = open(rgma_fname, O_WRONLY|O_CREAT|O_APPEND, 0777);
+               if (rgma_fd == -1) return;
+       }
+
+       slen = strlen(sqlstat);
+       iov[0].iov_base = &slen;
+       iov[0].iov_len = sizeof(slen);
+       iov[1].iov_base = sqlstat;
+       iov[1].iov_len = slen + 1;
+       if ((sysret = flock(rgma_fd, LOCK_SH)) != -1) {
+               sysret = writev(rgma_fd, iov, 2);
+               flock(rgma_fd, LOCK_UN);
+       }
+       
+       if (sysret == -1) return;
+
+       if (rgma_sock == -1) {
+               rgma_fname = getenv("EDG_WL_RGMA_SOCK");
+               if (rgma_fname == NULL) return;
+
+               if ((strlen(rgma_fname) + 1) > sizeof(rgma_saddr.sun_path))
+                       return ;
+
+               rgma_sock = socket(PF_UNIX, SOCK_DGRAM,0);
+               if (rgma_sock == -1) return;
+               
+               memset(&rgma_saddr, sizeof(rgma_saddr), 0);
+               rgma_saddr.sun_family = PF_UNIX;
+               strcpy(rgma_saddr.sun_path, rgma_fname);
+       }
+
+       sendto(rgma_sock, &slen, 1, 0,
+               (struct sockaddr*) &rgma_saddr, SUN_LEN(&rgma_saddr));
+
+       return;
+}
+
+void write2rgma_status(edg_wll_JobStat *stat)
+{
+       char *stmt = NULL;
+       char *string_jobid, *string_stat, *string_server;
+
+       string_jobid = edg_wlc_JobIdUnparse(stat->jobId);
+       string_stat = edg_wll_StatToString(stat->state);
+       string_server = edg_wlc_JobIdGetServer(stat->jobId);
+
+       trio_asprintf(&stmt, "INSERT INTO JobStatusRaw VALUES ('%|Ss','%s','%|Ss','%|Ss', '%d%03d')",
+                       string_jobid, string_stat, stat->owner, string_server,
+                       stat->stateEnterTime.tv_sec, stat->stateEnterTime.tv_usec/1000);
+
+       if (stmt) write2rgma_sql(stmt);
+
+       free(string_jobid);
+       free(string_stat);
+       free(string_server);
+       free(stmt);
+}
+
+#ifdef TEST
+int main(int argc, char* argv[])
+{
+       setenv("EDG_WL_RGMA_FILE", "/tmp/rgma_statefile", 1);
+       setenv("EDG_WL_RGMA_SOCK", "/tmp/rgma_statesock", 1);
+       write2rgma_sql("INSERT INTO JobStatusRaw VALUES ('Job_id9','CLEARED','Owner','BKServer', '1050024158210')");
+       return 0;
+}
+#endif
diff --git a/org.glite.lb/build.xml b/org.glite.lb/build.xml
new file mode 100755 (executable)
index 0000000..93faca8
--- /dev/null
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="lb" default="dist">
+       
+       <import file="../org.glite/project/baseline.properties.xml" />
+       <import file="./project/properties.xml"/>
+       <import file="${subsystem.properties.file}"/>
+       <import file="${global.properties.file}" />
+
+       <property file="${user.dependencies.file}"/>
+       <property file="${component.dependencies.file}" />
+       <property file="${subsystem.dependencies.file}" />
+       <property file="${global.dependencies.file}"/>
+       
+       <import file="${subsystem.taskdefs.file}" />
+       <import file="${global.taskdefs.file}" />
+
+       <import file="${global.targets-external-dependencies.file}"/>   
+       <import file="${global.targets-make.file}" />
+               
+       <property file="${module.version.file}"/>
+               
+       <target name="localinit"
+               description="Module specific initialization tasks">
+       </target>
+               
+       <target name="localcompile"
+               description="Module specific compile tasks">
+       </target>
+       
+       <target name="localclean"
+               description="Module specific cleaning tasks">
+       </target>
+       
+</project>             
diff --git a/org.glite.lb/project/MultiStruct.pm b/org.glite.lb/project/MultiStruct.pm
new file mode 100644 (file)
index 0000000..9cd847c
--- /dev/null
@@ -0,0 +1,191 @@
+package MultiStruct;
+
+use StructField;
+
+sub new {
+       shift;
+       my $self = {};
+       $self->{comments} = {}; # typ->comment
+       $self->{fields} = {};   # typ->{ name->StructField, ... }
+       $self->{order} = {};
+
+       bless $self;
+}
+
+sub selectType {
+       my $self = shift;
+       my $type = shift;
+       $self->{type} = $type;
+       1;
+}
+
+sub addType {
+       my $self = shift;
+       my $type = shift;
+       my $comment = shift;
+       $self->selectType($type);
+       $self->{comments}->{$type} = $comment;
+       $self->{fields}->{$type} = {};
+       1;
+}
+
+sub selectField {
+       my $self = shift;
+       $self->{field} = shift;
+       $self->getField;
+}
+
+sub addField {
+       my $self = shift;
+       my $field = shift;
+       
+       die "unselected type" unless $self->{type};
+       $self->{fields}->{$self->{type}}->{$field->{name}} = $field;
+       $self->selectField($field->{name});
+       1;
+}
+
+sub getField {
+       my $self = shift;
+       my $f = $self->{fields}->{$self->{type}}->{$self->{field}};
+       return $f ? $f : $self->{fields}->{_common_}->{$self->{field}};
+}
+
+sub load {
+       my $self = shift;
+       my $fh = shift;
+       local $_;
+
+       while ($_ = <$fh>) {
+
+               chomp;
+               s/#.*$//;
+               next if /^\s*$/;
+
+               if (/^\@type\s+(\S+)\s*(.*$)$/) {
+                       $self->addType($1,$2);
+                       $self->{order}->{$1} = $.;
+                       next;
+               }
+
+               s/^\s*//;
+               my ($ftype,$fname,$comment) = split /\s+/,$_,3;
+               if ($ftype eq '_code_') {
+                       my $f = $self->getField();
+                       addCode $f $fname,$comment;
+               }
+               elsif ($ftype eq '_alias_') {
+                       my $f = $self->getField();
+                       addAlias $f $fname,$comment;
+               }
+               elsif ($ftype eq '_special_') {
+                       my $f = $self->getField();
+                       addSpecial $f $fname;
+               }
+               elsif ($ftype eq '_null_') {
+                       my $f = $self->getField();
+                       setNull $f $fname;
+               }
+               elsif ($ftype eq '_optional_') {
+                       my $f = $self->getField();
+                       $f->{optional} = 1;
+               }
+               elsif ($ftype eq '_index_') {
+                       my $f = $self->getField();
+                       $f->{index} = 1;
+               }
+               else {
+                       my $f = new StructField $fname,$ftype,$comment,$.;
+                       $self->addField($f);
+               }
+       }
+}
+
+sub getTypes {
+       my $self = shift;
+       my @out;
+       local $_;
+
+       for (keys %{$self->{fields}}) {
+               push @out,$_ unless $_ eq '_common_';
+       }
+       @out;
+}
+
+sub getTypesOrdered {
+       my $self = shift;
+       my @names = getTypes $self;
+
+       sort {
+               my $oa = $self->{order}->{$a};
+               my $ob = $self->{order}->{$b};
+               $oa <=> $ob;
+       } @names;
+}
+
+sub getTypeComment {
+       my $self = shift;
+       my $type = shift || $self->{type};
+       $self->{comments}->{$type};
+}
+
+sub getFieldComment {
+       my $self = shift;
+       my $fname = shift;
+       $self->{fields}->{$self->{type}}->{$fname}->{comment};
+}
+
+sub getFields {
+       my $self = shift;
+       keys %{$self->{fields}->{$self->{type}}};
+}
+
+sub getFieldsOrdered {
+       my $self = shift;
+       my @names = $self->getFields;
+       sort {
+               my $oa = $self->selectField($a)->{order};
+               my $ob = $self->selectField($b)->{order};
+               $oa <=> $ob;
+       } @names;
+}
+
+sub getFieldOccurence {
+       my $self = shift;
+       my $fname = shift;
+       my @out;
+       local $_;
+
+       for (keys %{$self->{fields}}) {
+               push @out,$_ if $self->{fields}->{$_}->{$fname};
+       }
+       @out;
+}
+
+sub getAllFields {
+       my $self = shift;
+       my %out;
+       local $_;
+
+       for my $t (values %{$self->{fields}}) {
+               $out{$_->{name}} = 1 for (values %$t);
+       }
+       keys %out;
+}
+
+sub getAllFieldsOrdered {
+       my $self = shift;
+       my @names = getAllFields $self;
+
+       sort {
+               my @occ = $self->getFieldOccurence($a);
+               $self->selectType($occ[0]);
+               my $oa = $self->selectField($a)->{order};
+               @occ = $self->getFieldOccurence($b);
+               $self->selectType($occ[0]);
+               my $ob = $self->selectField($b)->{order};
+               $oa <=> $ob;
+       } @names;
+}
+
+1;
diff --git a/org.glite.lb/project/StructField.pm b/org.glite.lb/project/StructField.pm
new file mode 100644 (file)
index 0000000..95d33b8
--- /dev/null
@@ -0,0 +1,116 @@
+package StructField;
+
+$lang = 'C';
+1;
+
+sub new {
+       shift;
+       my $self = {};
+       $self->{name} = shift;
+       $self->{type} = shift;
+       $self->{comment} = shift;
+       $self->{order} = shift;
+       $self->{null} = $main::DefaultNullValue{$self->{type}};
+       bless $self;
+}
+
+sub addCode {
+       my $self = shift;
+       my $code = shift;
+       my $comment = shift;
+       push @{$self->{codes}},{name=>$code,comment=>$comment};
+       1;
+}
+
+sub addSpecial {
+       my $self = shift;
+       my $special = shift;
+       $self->{special} = $special;
+       1;
+}
+
+sub addAlias {
+       my $self = shift;
+       my $name = shift;
+       my $lang = shift;
+       $self->{aliases}->{$lang} = $name;
+       1;
+}
+
+sub hasAlias {
+       my $self = shift;
+       my $lang = shift;
+       return $self->{aliases}->{$lang} ? 1 : 0;
+}
+
+sub getName {
+       my $self = shift;
+       my $lang = shift || $lang;
+       $self->{aliases}->{$lang} || $self->{name};
+#      return $self->{aliases}->{$lang} ? $self->{aliases}->{$lang} : $self->{name};
+}
+
+sub getComment {
+       my $self = shift;
+       $self->{comment};
+}
+
+sub getDefaultNullValue {
+       my $self = shift;
+       $self->{null};
+}
+
+sub toString {
+       my $self = shift;
+       my $src = shift;
+       my $dst = shift;
+
+       eval $main::toString{$lang}->{$self->{type}};
+}
+
+sub fromString {
+       my $self = shift;
+       my $src = shift;
+       my $dst = shift;
+
+       eval $main::fromString{$lang}->{$self->{type}};
+}
+
+sub isNULL {
+       my $self = shift;
+       my $a = shift;
+       my $b = $self->{null};
+
+       eval $main::compare{$lang}->{$self->{type}};
+}
+
+sub isnotNULL {
+       my $self = shift;
+       my $src = shift;
+
+       '!('.$self->isNULL($src).')';
+}
+
+sub compare {
+       my $self = shift;
+       my $a = shift;
+       my $b = shift;
+       eval $main::compare{$lang}->{$self->{type}};
+}
+
+sub toFormatString {
+       my $self = shift;
+
+       eval $main::toFormatString{$lang}->{$self->{type}};
+}
+
+sub setNull {
+       my $self = shift;
+       $self->{null} = shift;
+}
+
+sub getType {
+       my $self = shift;
+
+       eval $main::types{$lang}->{$self->{type}};
+}
diff --git a/org.glite.lb/project/at3 b/org.glite.lb/project/at3
new file mode 100755 (executable)
index 0000000..8ff52ec
--- /dev/null
@@ -0,0 +1,93 @@
+#!/usr/bin/perl -w
+
+use File::Basename;
+my $dir;
+BEGIN{
+       $dir = dirname $0;
+}
+
+my $lines = $ENV{AT3_LINES};
+
+use lib $dir;
+use MultiStruct;
+require 'types.T';
+
+my $eventsn;
+for (@INC) { 
+       if (-f "$_/events.T") {
+               $eventsn="$_/events.T";
+               last;
+       }
+}
+
+my $statusn;
+for (@INC) {
+       if (-f "$_/status.T") {
+               $statusn = "$_/status.T";
+               last;
+       }
+}
+
+my $indent = '';
+
+my $event = new MultiStruct;
+my $status = new MultiStruct;
+
+sub gen {
+       local $_ = shift;
+
+       s/^\n!//;
+       s/\n!/\n/g;
+       print $_;
+}
+
+
+open EVENTS,$eventsn or die "$eventsn: $!\n";
+$event->load(\*EVENTS);
+close EVENTS;
+
+open STATUS,$statusn or die "$statusn: $!\n";
+$status->load(\*STATUS);
+close STATUS;
+
+my $code;
+my $startcode;
+while (<>) {
+       chomp;
+       if (/^\@\@\@LANG: (\S+)$/) {
+               $StructField::lang = $1;
+               next;
+       }
+
+       if ($code) {
+               if (/^\@\@\@}$/) {
+                       $code .= "1;\n";
+                       print "#line $startcode \"$ARGV\"\n/* begin */\n" if $lines;
+                       eval $code or warn "eval: $@ at $ARGV:$.\n";
+                       my $nxtline = $.+1;
+                       print "/* end */\n#line $nxtline \"$ARGV\"\n" if $lines;
+                       undef $code;
+               }
+               else { $code .= $_."\n"; }
+       }
+       else {
+               if (/^\@\@\@{$/) {
+                       $startcode = $.;
+                       $code = "\n";
+               }
+               elsif (/^\@\@\@AUTO$/) {
+                       print qq{
+  !! Automatically generated file
+  !! Do not edit, your changes will be discarded upon build
+  !! Change the corresponding template file $ARGV
+
+};
+                       print "#line $. \"$ARGV\"\n" if $lines;
+               }
+               else {
+                       print "$_\n";
+               }
+       }
+}
+
+# print $event_common{prog}->copy('bla','hu');
diff --git a/org.glite.lb/project/dependencies.properties b/org.glite.lb/project/dependencies.properties
new file mode 100644 (file)
index 0000000..dda0b85
--- /dev/null
@@ -0,0 +1,3 @@
+org.glite.version = HEAD
+org.glite.wms.thirdparty-globus_ssl_utils.version = HEAD
+org.glite.wms.jobid.version = HEAD
diff --git a/org.glite.lb/project/events.T b/org.glite.lb/project/events.T
new file mode 100644 (file)
index 0000000..b8e5399
--- /dev/null
@@ -0,0 +1,183 @@
+@type _common_
+       timeval timestamp       timestamp of event generation
+       _alias_ date            ULM
+       timeval arrived         timestamp of event store
+       _alias_ arr_date        ULM
+       _optional_
+       string  host            hostname of the machine where the event was generated
+       _alias_ host            ULM
+       int     level           logging level (system, debug, ...)
+       _alias_ lvl             ULM
+       _code_  EMERGENCY       emergency
+       _code_  ALERT           alert
+       _code_  ERROR           error
+       _code_  WARNING         warning
+       _code_  AUTH            authentication
+       _code_  SECURITY        security
+       _code_  USAGE           usage
+       _code_  SYSTEM          system
+       _code_  IMPORTANT       important
+       _code_  DEBUG           debug
+       int     priority        message priority (yet 0 for asynchronous and 1 for synchronous transfers)
+       _null_  -1
+       jobid   jobId           DataGrid job id of the source job
+       string  seqcode         sequence code assigned to the event
+       string  user            identity (cert. subj.) of the generator
+       logsrc  source          source (WMS component) which generated this event
+#      string  prog            name of program ("EDG WMS" of name of the application)
+       string  src_instance    instance of WMS component (e.g. service communication endpoint)
+       _optional_
+
+@type Transfer         Start, success, or failure of job transfer to another component
+       logsrc  destination     destination where the job is being transfered to
+       string  dest_host       destination hostname
+       string  dest_instance   destination instance
+       _optional_
+       string  job             job description in receiver language
+       int     result          result of the attempt
+       _code_  START           the sending component has started or is about to start the transfer
+       _code_  OK              job was sent successfully
+       _code_  REFUSED         job was refused by the other component
+       _code_  FAIL            transfer failed for other reason than explicit refusal (eg. network timeout)
+       string  reason          detailed description of transfer, especially reason of failure
+       _optional_
+       string  dest_jobid      destination internal jobid
+       _optional_
+
+@type Accepted         Accepting job (successful couterpart to Transfer)
+       logsrc  from            where was the job received from
+       string  from_host       sending component hostname
+       string  from_instance   sending component instance
+       _optional_
+       string  local_jobid     new jobId (Condor, Globus ...) assigned by the receiving component
+
+@type Refused          Refusing job (unsuccessful couterpart to Transfer)
+       logsrc  from            where was the job received from
+       string  from_host       sending component hostname
+       string  from_instance   sending component instance
+       _optional_
+       string  reason          reason of refusal
+
+@type EnQueued         The job has been enqueued in an inter-component queue
+       string  queue           destination queue
+       string  job             job description in receiver language
+       int     result          result of the attempt
+       _code_  START           the sending component has started or is about to start the transfer
+       _code_  OK              job was sent successfully
+       _code_  REFUSED         job was refused by the other component
+       _code_  FAIL            transfer failed for other reason than explicit refusal (eg. network timeout)
+       string  reason          detailed description of transfer, especially reason of failure
+
+@type DeQueued         The job has been dequeued from an inter-component queue
+       string  queue           queue name
+       string  local_jobid     new jobId assigned by the receiving component
+
+@type HelperCall       Helper component is called
+       string  helper_name     name of the called component
+       string  helper_params   parameters of the call
+       int     src_role        whether the logging component is called or calling one
+       _code_  CALLING         the logging component is caller
+       _code_  CALLED          the logging component is callee
+
+@type HelperReturn     Helper component is returning the control
+       string  helper_name     name of the called component
+       string  retval          returned data
+       int     src_role        whether the logging component is called or calling one
+       _code_  CALLING         the logging component is caller
+       _code_  CALLED          the logging component is callee
+
+@type Running          Executable started
+       string  node            worker node where the executable is run
+
+@type Resubmission     Result of resubmission decision
+       int     result          result code
+       _code_  WILLRESUB       will be resubmitted
+       _code_  WONTRESUB       will not be resubmitted
+       string  reason          reason for the decision
+       string  tag             value of the attribute on which the decision is based
+
+@type Done             Execution terminated (normally or abnormally)
+       int     status_code     way of termination
+       _code_  OK              terminated by itself
+       _code_  FAILED          disappeared from LRMS
+       _code_  CANCELLED       cancelled by user request
+       string  reason          reason for the change
+       int     exit_code       process exit code
+       _null_  -1
+
+@type Cancel           Cancel operation has been attempted on the job
+       int     status_code     classification of the cancel
+       _code_  REQ             request acknowledged
+       _code_  REFUSE          request declined by this component
+       _code_  DONE            request completed by whole WMS
+       _code_  ABORT           request refused by whole WMS
+       string  reason  detailed description
+
+@type Abort            Job aborted by system
+       string  reason          reason of abort
+
+@type Clear            Job cleared, output sandbox removed
+       int     reason          why the job was cleared
+       _code_  USER            user retrieved output sandbox
+       _code_  TIMEOUT         timed out, resource purge forced
+       _code_  NOOUTPUT        no output was generated
+
+@type Purge            Job is purged from bookkepping server
+
+@type Match            Matching CE found
+       string  dest_id         Id of the destination CE/queue
+       
+@type Pending          No match found yet
+       string  reason          why matching CE cannot be found
+
+@type RegJob           New job registration
+       string  jdl             job description
+       string  ns              NetworkServer handling the job
+       jobid   parent          jobid of parent job
+       _optional_
+
+       int     jobtype         job type
+       _code_  SIMPLE          simple job
+       _code_  DAG             dag (containing static set of subjobs)
+       _code_  PARTITIONABLE   partitionable (may become partitioned)
+       _code_  PARTITIONED     partitioned (dynamically created dag)
+       
+       int     nsubjobs        number of subjobs
+       _optional_
+       string  seed            seed for subjob id generation
+       _optional_
+
+@type Chkpt            Application-specific checkpoint record
+       string  tag             checkpoint tag
+       string  classad         checkpoint value
+
+@type Listener                 Listening network port for interactive control
+       string  svc_name        port instance name
+       string  svc_host        hostname
+       port    svc_port        port number
+
+@type CurDescr         current state of job processing (optional event)
+       string  descr           description of current job transformation (output of helper)
+
+@type UserTag          user tag -- arbitrary name=value pair
+       string  name    tag name
+       string  value   tag value
+
+@type ChangeACL                Management of ACL stored on bookkepping server
+       string  user_id         DN or VOMS parameter (in format VO:group)
+       int     user_id_type    type of information given in user_id (DN or VOMS)
+       _null_  -1
+       int     permission      ACL permission to change (currently only READ)
+       _null_  -1
+       int     permission_type type of permission requested ('allow', 'deny')
+       _null_  -1
+       int     operation       operation requested to perform with ACL (add, remove)
+       _null_  -1
+
+@type Notification     Management of notification service
+       notifid notifId         notification id
+       string  owner           owner
+       string  dest_host       destination host
+       port    dest_port       destination port
+       string  jobstat         job status
+
diff --git a/org.glite.lb/project/properties.xml b/org.glite.lb/project/properties.xml
new file mode 100755 (executable)
index 0000000..1ad8900
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB Subsystem common properties">
+       <property name="subsystem.build.properties.file" value="./project/build.properties" />
+       <property file="${subsystem.build.properties.file}" />  
+       <property name="subsystem.name" value="${lb.subsystem.name}"/>
+       <property name="subsystem.prefix" value="${lb.subsystem.prefix}"/>
+
+       <import file="${subsystem.general.properties.file}" />
+
+       <!--  XXX: should be resolved at project level -->
+       <property name="ext.ares.subdir" value="ares" />
+       <property name="ext.expat.subdir" value="expat" />
+       <property name="ext.voms.subdir" value="voms" />
+       <property name="ext.gacl.subdir" value="gacl" />
+       <property name="ext.mysql.subdir" value="mysql" />
+</project>
diff --git a/org.glite.lb/project/status.T b/org.glite.lb/project/status.T
new file mode 100644 (file)
index 0000000..10071ac
--- /dev/null
@@ -0,0 +1,77 @@
+@type _common_
+jobid  jobId           Id of the job
+string owner           Job owner
+_index_
+
+int    jobtype         Type of job
+       _null_  -1
+       _code_ SIMPLE   simple job
+       _code_ DAG      composite job
+jobid  parent_job      parent job of subjob
+
+string seed            string used for generation of subjob IDs
+int    children_num    number of subjobs
+strlist        children        list of subjob IDs
+       _special_       XMLstructured
+intlist        children_hist   summary (histogram) of children job states
+       _special_       XMLstructured
+stslist children_states full status information of the children
+       _special_       XMLstructured
+
+string         condorId        Id within Condor-G
+string globusId        Globus allocated Id
+string localId         Id within LRMS
+
+string jdl             User submitted job description
+string matched_jdl     Full job description after matchmaking
+string destination     ID of CE where the job is being sent
+_index_
+string condor_jdl      ClassAd passed to Condor-G for last job execution
+string rsl             Job RSL sent to Globus
+
+string reason          Reason of being in this status, if any
+
+string location        Where the job is being processed
+_index_
+string ce_node         Worker node where the job is executed
+string network_server  Network server handling the job
+
+bool   subjob_failed   Subjob failed (the parent job will fail too)
+int    done_code               Return code
+       _null_  -1
+       _code_  OK              Finished correctly
+       _code_  FAILED          Execution failed
+       _code_  CANCELLED       Cancelled by user
+int    exit_code               Unix exit code
+bool   resubmitted     The job was resubmitted
+
+bool   cancelling      Cancellation request in progress
+string cancelReason    Reason of cancel
+
+int    cpuTime         Consumed CPU time
+       _null_  -1
+
+taglist        user_tags       List of pairs (user_tag, user_value)
+       _special_       XMLstructured
+
+timeval        stateEnterTime  When entered this status
+timeval        lastUpdateTime  Last known event of the job
+
+intlist        stateEnterTimes When all previous states were entered
+       _special_       XMLstructured
+
+bool   expectUpdate    Some logged information has not arrived yet
+string expectFrom      Sources of the missing information
+string acl             ACL of the job
+
+@type Submitted                entered by the user to the User Interface or registered by Job Partitioner
+@type Waiting          Accepted by WMS, waiting for resource allocation
+@type Ready            Matching resources found
+@type Scheduled                Accepted by LRMS queue
+@type Running          Executable is running
+@type Done             Execution finished, output is available
+@type Cleared          Output transfered back to user and freed
+@type Aborted          Aborted by system (at any stage)
+@type Cancelled                Cancelled by user
+@type Unknown          Status cannot be determined
+@type Purged           Job has been purged from bookkeeping server (for LB->RGMA interface)
diff --git a/org.glite.lb/project/taskdefs.xml b/org.glite.lb/project/taskdefs.xml
new file mode 100755 (executable)
index 0000000..1f92bef
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project name="LB Subsystem common tasks and types definitions">
+       <target name="lbmakefiles">
+               <exec executable="ln" failonerror="true">
+                       <arg line="-fs ${component.dir}/Makefile ${module.build.dir}/Makefile"/>
+               </exec>
+               <echo file="${module.build.dir}/Makefile.inc">
+repository=${repository}
+platform=${platform}
+globus=${ext.globus.subdir}
+lbproject=${subsystem.project.dir}
+lbconfig=${subsystem.dir}/config
+src=${module.src.dir}
+interface=${module.int.dir}
+autosrc=${module.autosrc.dir}
+stageinc=${stage.inc.dir}
+stagelib=${stage.lib.dir}
+stagebin=${stage.bin.dir}
+globalprefix=${global.prefix}
+lbprefix=${subsystem.prefix}
+installdir=${install.dir}
+globusflavour=gcc32dbg
+ares=${ext.ares.subdir}
+expat=${ext.expat.subdir}
+voms=${ext.voms.subdir}
+gacl=${ext.gacl.subdir}
+mysql=${ext.mysql.subdir}
+</echo>
+       </target>
+
+       <target name="clean-autotools">
+       </target>
+</project>
diff --git a/org.glite.lb/project/types.T b/org.glite.lb/project/types.T
new file mode 100644 (file)
index 0000000..c4a4f7f
--- /dev/null
@@ -0,0 +1,108 @@
+%types = (
+       C=>{
+               bool=>'"int"',
+               string=>'"char *"',
+               strlist=>'"char **"',
+               intlist=>'"int *"',
+               taglist=>'"edg_wll_TagValue *"',
+               stslist=>'"struct _edg_wll_JobStat *"',
+               timeval=>'"struct timeval"',
+               jobid=>'"edg_wlc_JobId"',
+               notifid=>'"edg_wll_NotifId"',
+               logsrc=>'"edg_wll_Source"',
+               port=>'"uint16_t"',
+#              level=>'"enum edg_wll_Level"',
+               int=>'"int"'
+       },
+       'C++'=>{
+               string=>'"std::string"',
+               timeval=>'"struct timeval"',
+               jobid=>'"edg::workload::common::jobid::JobId"',
+               bool=>'"int"',
+                intlist=>'"std::vector<int>"',
+                strlist=>'"std::vector<std::string>"',
+                taglist=>'"std::vector<std::pair<std::string>>"',
+               stslist=>'"std::vector<JobStatus>"',
+               logsrc=>'"int"',
+               port=>'"int"',
+               int=>'"int"'
+       }
+);
+
+%toString = (
+       C=>{
+               int=>'qq{asprintf(&$dst,"%d",$src);}',
+               port=>'qq{asprintf(&$dst,"%d",(int) $src);}',
+               bool=>'qq{asprintf(&$dst,"%d",$src);}',
+               string=>'qq{$dst = $src?strdup($src):NULL;}',
+               timeval=>'qq{edg_wll_ULMTimevalToDate(($src).tv_sec,($src).tv_usec,$dst);}',
+               jobid=>'qq{$dst = edg_wlc_JobIdUnparse($src);}',
+               notifid=>'qq{$dst = edg_wll_NotifIdUnparse($src);}',
+#              level=>'qq{$dst = edg_wll_LevelToString($src);}',
+               logsrc=>'qq{$dst = edg_wll_SourceToString($src);}',
+#      strlist, intlist, stslist are used only in consumer API, they don't need toString method
+       }
+);
+
+%ULMasString = (
+       logsrc=>1
+);
+
+%fromString = (
+       C=>{
+               int=>'qq{$dst = atoi($src);}',
+               port=>'qq{$dst = (uint16_t) atoi($src);}',
+               bool=>'qq{$dst = atoi($src);}',
+               string=>'qq{$dst = strdup($src);}',
+               timeval=>'qq{edg_wll_ULMDateToTimeval($src,&$dst);}',
+               jobid=>'qq{edg_wlc_JobIdParse($src,&$dst);}',
+               notifid=>'qq{edg_wll_NotifIdParse($src,&$dst);}',
+#              level=>'qq{$dst = edg_wll_StringToLevel($src);}',
+               logsrc=>'qq{$dst = edg_wll_StringToSource($src);}',
+#      strlist, intlist, stslist are used only in consumer API, they don't need fromString method
+       }
+);
+
+%DefaultNullValue = (
+       int=>0,
+       port=>0,
+#      level=>'EDG_WLL_LEVEL_UNDEFINED',
+       bool=>0,
+       string=>'NULL',
+       jobid=>'NULL',
+       notifid=>'NULL',
+       logsrc=>'EDG_WLL_SOURCE_NONE',
+       timeval=>'null_timeval',
+       strlist=>'NULL',
+       intlist=>'NULL',
+       taglist=>'NULL',
+       stslist=>'NULL',
+);
+
+%compare = (
+       C=>{
+               int=>'"($a == $b)"',
+               port=>'"($a == $b)"',
+#              level=>'"($a == $b)"',
+               bool=>'"(($a || !$b) && ($b || !$a))"',
+               string=>'"(($a) == NULL && ($b) == NULL) || (($a)&&($b)&& !strcmp($a,$b))"',
+               jobid=>'"($a) == ($b)"',
+               notifid=>'"($a) == ($b)"',
+               logsrc=>'"($a) == ($b)"',
+               timeval=>'"($a).tv_sec == ($b).tv_sec && ($a).tv_usec == ($b).tv_usec"',
+       }
+);
+
+%toFormatString = (
+       C=>{
+               int=>'"%d"',
+               port=>'"%d"',
+               bool=>'"%d"',
+#              level=>'"%s"',
+               string=>'"%|Us"',
+               jobid=>'"%s"',
+               notifid=>'"%s"',
+               logsrc=>'"%s"',
+               timeval=>'"%s"',
+       }
+);