From 2eaacd549ac974644664db6a2b86748f05db8c09 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Ale=C5=A1=20K=C5=99enek?= Date: Fri, 18 Jun 2004 11:45:49 +0000 Subject: [PATCH] initial import --- org.glite.lb.client-interface/Makefile | 33 + org.glite.lb.client-interface/build.xml | 36 + org.glite.lb.client-interface/interface/consumer.h | 290 + org.glite.lb.client-interface/interface/context.h | 214 + org.glite.lb.client-interface/interface/dump.h | 31 + org.glite.lb.client-interface/interface/events.h.T | 332 ++ .../interface/jobstat.h.T | 141 + org.glite.lb.client-interface/interface/load.h | 27 + .../interface/notification.h | 157 + org.glite.lb.client-interface/interface/notifid.h | 4 + .../interface/producer.h.T | 385 ++ org.glite.lb.client-interface/interface/purge.h | 59 + .../project/properties.xml | 12 + org.glite.lb.client/.Makefile.swp | Bin 0 -> 12288 bytes org.glite.lb.client/Makefile | 91 + org.glite.lb.client/build.xml | 36 + org.glite.lb.client/interface/CountRef.h | 48 + org.glite.lb.client/interface/Event.h.T | 147 + org.glite.lb.client/interface/Job.h | 73 + org.glite.lb.client/interface/JobStatus.h.T | 152 + org.glite.lb.client/interface/LoggingExceptions.h | 144 + org.glite.lb.client/interface/Notification.h | 76 + org.glite.lb.client/interface/ServerConnection.h | 306 ++ org.glite.lb.client/project/properties.xml | 12 + org.glite.lb.client/project/taskdefs.xml | 4 + org.glite.lb.client/src/Event.cpp.T | 337 ++ org.glite.lb.client/src/Job.cpp | 209 + org.glite.lb.client/src/JobStatus.cpp.T | 481 ++ org.glite.lb.client/src/Notification.cpp | 276 + org.glite.lb.client/src/ServerConnection.cpp | 1302 +++++ org.glite.lb.client/src/args.c.T | 533 ++ org.glite.lb.client/src/args.h | 33 + org.glite.lb.client/src/connection.c | 263 + org.glite.lb.client/src/connection.h | 12 + org.glite.lb.client/src/consumer.c | 429 ++ org.glite.lb.client/src/dump.c | 233 + org.glite.lb.client/src/load.c | 210 + org.glite.lb.client/src/logevent.c.T | 272 + org.glite.lb.client/src/notification.c | 743 +++ org.glite.lb.client/src/prod_proto.c | 152 + org.glite.lb.client/src/prod_proto.h | 28 + org.glite.lb.client/src/producer.c | 594 ++ org.glite.lb.client/src/purge.c | 392 ++ org.glite.lb.client/src/uiwrap.c.T | 102 + org.glite.lb.common/Makefile | 69 + org.glite.lb.common/build.xml | 36 + org.glite.lb.common/interface/authz.h | 24 + org.glite.lb.common/interface/context-int.h | 152 + org.glite.lb.common/interface/dgssl.h | 204 + org.glite.lb.common/interface/escape.h | 60 + org.glite.lb.common/interface/events_parse.h | 83 + org.glite.lb.common/interface/il_string.h | 30 + org.glite.lb.common/interface/log_proto.h | 76 + org.glite.lb.common/interface/mini_http.h | 43 + org.glite.lb.common/interface/trio.h | 187 + org.glite.lb.common/interface/ulm_parse.h | 47 + org.glite.lb.common/interface/xml_conversions.h | 132 + org.glite.lb.common/interface/xml_parse.h | 58 + org.glite.lb.common/project/properties.xml | 12 + org.glite.lb.common/src/context.c | 419 ++ org.glite.lb.common/src/dgssl.c | 679 +++ org.glite.lb.common/src/escape.c | 221 + org.glite.lb.common/src/events.c.T | 349 ++ org.glite.lb.common/src/events_parse.c.T | 732 +++ org.glite.lb.common/src/il_int.c | 69 + org.glite.lb.common/src/il_log.c | 32 + org.glite.lb.common/src/il_msg.c | 53 + org.glite.lb.common/src/il_string.c | 61 + org.glite.lb.common/src/mini_http.c | 224 + org.glite.lb.common/src/notifid.c | 88 + org.glite.lb.common/src/notifid.h | 29 + org.glite.lb.common/src/param.c | 429 ++ org.glite.lb.common/src/query_rec.c | 50 + org.glite.lb.common/src/status.c.T | 172 + org.glite.lb.common/src/strio.c | 581 ++ org.glite.lb.common/src/strio.h | 227 + org.glite.lb.common/src/trio.c | 5708 ++++++++++++++++++++ org.glite.lb.common/src/triop.h | 138 + org.glite.lb.common/src/ulm_parse.c | 410 ++ org.glite.lb.common/src/xml_conversions.c | 835 +++ org.glite.lb.common/src/xml_parse.c.T | 2613 +++++++++ org.glite.lb.server/Makefile | 79 + org.glite.lb.server/build.xml | 36 + org.glite.lb.server/project/properties.xml | 12 + org.glite.lb.server/src/bkindex.c | 305 ++ org.glite.lb.server/src/bkserverd.c | 1308 +++++ org.glite.lb.server/src/db_store.c | 91 + org.glite.lb.server/src/dump.c | 192 + org.glite.lb.server/src/get_events.c.T | 157 + org.glite.lb.server/src/get_events.h | 24 + org.glite.lb.server/src/il_notification.c | 388 ++ org.glite.lb.server/src/il_notification.h | 97 + org.glite.lb.server/src/index.c.T | 227 + org.glite.lb.server/src/index.h | 27 + org.glite.lb.server/src/index_lex.l | 47 + org.glite.lb.server/src/index_parse.y | 242 + org.glite.lb.server/src/jobstat.c | 1250 +++++ org.glite.lb.server/src/jobstat.h | 30 + org.glite.lb.server/src/jobstat_supp.c | 669 +++ org.glite.lb.server/src/lb_authz.c | 765 +++ org.glite.lb.server/src/lb_authz.h | 42 + org.glite.lb.server/src/lb_html.c | 146 + org.glite.lb.server/src/lb_html.h | 13 + org.glite.lb.server/src/lb_http.c | 53 + org.glite.lb.server/src/lb_http.h | 12 + org.glite.lb.server/src/lb_proto.c | 565 ++ org.glite.lb.server/src/lb_proto.h | 27 + org.glite.lb.server/src/lb_xml_parse.c.T | 1731 ++++++ org.glite.lb.server/src/lb_xml_parse.h | 32 + org.glite.lb.server/src/lbs_db.c | 219 + org.glite.lb.server/src/lbs_db.h | 85 + org.glite.lb.server/src/load.c | 210 + org.glite.lb.server/src/lock.c | 32 + org.glite.lb.server/src/lock.h | 4 + org.glite.lb.server/src/notif_match.c | 166 + org.glite.lb.server/src/notification.c | 436 ++ org.glite.lb.server/src/openserver.c | 18 + org.glite.lb.server/src/purge.h | 49 + org.glite.lb.server/src/query.c | 1414 +++++ org.glite.lb.server/src/query.h | 3 + org.glite.lb.server/src/request.c | 96 + org.glite.lb.server/src/server_state.c | 54 + org.glite.lb.server/src/server_state.h | 10 + org.glite.lb.server/src/srv_purge.c | 561 ++ org.glite.lb.server/src/store.c.T | 566 ++ org.glite.lb.server/src/store.h | 49 + org.glite.lb.server/src/stored_master.c | 58 + org.glite.lb.server/src/userjobs.c | 77 + org.glite.lb.server/src/write2rgma.c | 110 + org.glite.lb/build.xml | 35 + org.glite.lb/project/MultiStruct.pm | 191 + org.glite.lb/project/StructField.pm | 116 + org.glite.lb/project/at3 | 93 + org.glite.lb/project/dependencies.properties | 3 + org.glite.lb/project/events.T | 183 + org.glite.lb/project/properties.xml | 17 + org.glite.lb/project/status.T | 77 + org.glite.lb/project/taskdefs.xml | 34 + org.glite.lb/project/types.T | 108 + 139 files changed, 38384 insertions(+) create mode 100644 org.glite.lb.client-interface/Makefile create mode 100755 org.glite.lb.client-interface/build.xml create mode 100644 org.glite.lb.client-interface/interface/consumer.h create mode 100644 org.glite.lb.client-interface/interface/context.h create mode 100644 org.glite.lb.client-interface/interface/dump.h create mode 100644 org.glite.lb.client-interface/interface/events.h.T create mode 100644 org.glite.lb.client-interface/interface/jobstat.h.T create mode 100644 org.glite.lb.client-interface/interface/load.h create mode 100644 org.glite.lb.client-interface/interface/notification.h create mode 100644 org.glite.lb.client-interface/interface/notifid.h create mode 100644 org.glite.lb.client-interface/interface/producer.h.T create mode 100644 org.glite.lb.client-interface/interface/purge.h create mode 100755 org.glite.lb.client-interface/project/properties.xml create mode 100644 org.glite.lb.client/.Makefile.swp create mode 100644 org.glite.lb.client/Makefile create mode 100755 org.glite.lb.client/build.xml create mode 100644 org.glite.lb.client/interface/CountRef.h create mode 100644 org.glite.lb.client/interface/Event.h.T create mode 100644 org.glite.lb.client/interface/Job.h create mode 100644 org.glite.lb.client/interface/JobStatus.h.T create mode 100644 org.glite.lb.client/interface/LoggingExceptions.h create mode 100644 org.glite.lb.client/interface/Notification.h create mode 100644 org.glite.lb.client/interface/ServerConnection.h create mode 100755 org.glite.lb.client/project/properties.xml create mode 100755 org.glite.lb.client/project/taskdefs.xml create mode 100644 org.glite.lb.client/src/Event.cpp.T create mode 100644 org.glite.lb.client/src/Job.cpp create mode 100644 org.glite.lb.client/src/JobStatus.cpp.T create mode 100644 org.glite.lb.client/src/Notification.cpp create mode 100644 org.glite.lb.client/src/ServerConnection.cpp create mode 100644 org.glite.lb.client/src/args.c.T create mode 100644 org.glite.lb.client/src/args.h create mode 100644 org.glite.lb.client/src/connection.c create mode 100644 org.glite.lb.client/src/connection.h create mode 100644 org.glite.lb.client/src/consumer.c create mode 100644 org.glite.lb.client/src/dump.c create mode 100644 org.glite.lb.client/src/load.c create mode 100644 org.glite.lb.client/src/logevent.c.T create mode 100644 org.glite.lb.client/src/notification.c create mode 100644 org.glite.lb.client/src/prod_proto.c create mode 100644 org.glite.lb.client/src/prod_proto.h create mode 100644 org.glite.lb.client/src/producer.c create mode 100644 org.glite.lb.client/src/purge.c create mode 100644 org.glite.lb.client/src/uiwrap.c.T create mode 100644 org.glite.lb.common/Makefile create mode 100755 org.glite.lb.common/build.xml create mode 100644 org.glite.lb.common/interface/authz.h create mode 100644 org.glite.lb.common/interface/context-int.h create mode 100644 org.glite.lb.common/interface/dgssl.h create mode 100644 org.glite.lb.common/interface/escape.h create mode 100644 org.glite.lb.common/interface/events_parse.h create mode 100644 org.glite.lb.common/interface/il_string.h create mode 100644 org.glite.lb.common/interface/log_proto.h create mode 100644 org.glite.lb.common/interface/mini_http.h create mode 100644 org.glite.lb.common/interface/trio.h create mode 100644 org.glite.lb.common/interface/ulm_parse.h create mode 100644 org.glite.lb.common/interface/xml_conversions.h create mode 100644 org.glite.lb.common/interface/xml_parse.h create mode 100755 org.glite.lb.common/project/properties.xml create mode 100644 org.glite.lb.common/src/context.c create mode 100644 org.glite.lb.common/src/dgssl.c create mode 100644 org.glite.lb.common/src/escape.c create mode 100644 org.glite.lb.common/src/events.c.T create mode 100644 org.glite.lb.common/src/events_parse.c.T create mode 100644 org.glite.lb.common/src/il_int.c create mode 100644 org.glite.lb.common/src/il_log.c create mode 100644 org.glite.lb.common/src/il_msg.c create mode 100644 org.glite.lb.common/src/il_string.c create mode 100644 org.glite.lb.common/src/mini_http.c create mode 100644 org.glite.lb.common/src/notifid.c create mode 100644 org.glite.lb.common/src/notifid.h create mode 100644 org.glite.lb.common/src/param.c create mode 100644 org.glite.lb.common/src/query_rec.c create mode 100644 org.glite.lb.common/src/status.c.T create mode 100644 org.glite.lb.common/src/strio.c create mode 100644 org.glite.lb.common/src/strio.h create mode 100644 org.glite.lb.common/src/trio.c create mode 100644 org.glite.lb.common/src/triop.h create mode 100644 org.glite.lb.common/src/ulm_parse.c create mode 100644 org.glite.lb.common/src/xml_conversions.c create mode 100644 org.glite.lb.common/src/xml_parse.c.T create mode 100644 org.glite.lb.server/Makefile create mode 100755 org.glite.lb.server/build.xml create mode 100755 org.glite.lb.server/project/properties.xml create mode 100644 org.glite.lb.server/src/bkindex.c create mode 100644 org.glite.lb.server/src/bkserverd.c create mode 100644 org.glite.lb.server/src/db_store.c create mode 100644 org.glite.lb.server/src/dump.c create mode 100644 org.glite.lb.server/src/get_events.c.T create mode 100644 org.glite.lb.server/src/get_events.h create mode 100644 org.glite.lb.server/src/il_notification.c create mode 100644 org.glite.lb.server/src/il_notification.h create mode 100644 org.glite.lb.server/src/index.c.T create mode 100644 org.glite.lb.server/src/index.h create mode 100644 org.glite.lb.server/src/index_lex.l create mode 100644 org.glite.lb.server/src/index_parse.y create mode 100644 org.glite.lb.server/src/jobstat.c create mode 100644 org.glite.lb.server/src/jobstat.h create mode 100644 org.glite.lb.server/src/jobstat_supp.c create mode 100644 org.glite.lb.server/src/lb_authz.c create mode 100644 org.glite.lb.server/src/lb_authz.h create mode 100644 org.glite.lb.server/src/lb_html.c create mode 100644 org.glite.lb.server/src/lb_html.h create mode 100644 org.glite.lb.server/src/lb_http.c create mode 100644 org.glite.lb.server/src/lb_http.h create mode 100644 org.glite.lb.server/src/lb_proto.c create mode 100644 org.glite.lb.server/src/lb_proto.h create mode 100644 org.glite.lb.server/src/lb_xml_parse.c.T create mode 100644 org.glite.lb.server/src/lb_xml_parse.h create mode 100644 org.glite.lb.server/src/lbs_db.c create mode 100644 org.glite.lb.server/src/lbs_db.h create mode 100644 org.glite.lb.server/src/load.c create mode 100644 org.glite.lb.server/src/lock.c create mode 100644 org.glite.lb.server/src/lock.h create mode 100644 org.glite.lb.server/src/notif_match.c create mode 100644 org.glite.lb.server/src/notification.c create mode 100644 org.glite.lb.server/src/openserver.c create mode 100644 org.glite.lb.server/src/purge.h create mode 100644 org.glite.lb.server/src/query.c create mode 100644 org.glite.lb.server/src/query.h create mode 100644 org.glite.lb.server/src/request.c create mode 100644 org.glite.lb.server/src/server_state.c create mode 100644 org.glite.lb.server/src/server_state.h create mode 100644 org.glite.lb.server/src/srv_purge.c create mode 100644 org.glite.lb.server/src/store.c.T create mode 100644 org.glite.lb.server/src/store.h create mode 100644 org.glite.lb.server/src/stored_master.c create mode 100644 org.glite.lb.server/src/userjobs.c create mode 100755 org.glite.lb.server/src/write2rgma.c create mode 100755 org.glite.lb/build.xml create mode 100644 org.glite.lb/project/MultiStruct.pm create mode 100644 org.glite.lb/project/StructField.pm create mode 100755 org.glite.lb/project/at3 create mode 100644 org.glite.lb/project/dependencies.properties create mode 100644 org.glite.lb/project/events.T create mode 100755 org.glite.lb/project/properties.xml create mode 100644 org.glite.lb/project/status.T create mode 100755 org.glite.lb/project/taskdefs.xml create mode 100644 org.glite.lb/project/types.T diff --git a/org.glite.lb.client-interface/Makefile b/org.glite.lb.client-interface/Makefile new file mode 100644 index 0000000..0d501a0 --- /dev/null +++ b/org.glite.lb.client-interface/Makefile @@ -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 index 0000000..4aa4105 --- /dev/null +++ b/org.glite.lb.client-interface/build.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.lb.client-interface/interface/consumer.h b/org.glite.lb.client-interface/interface/consumer.h new file mode 100644 index 0000000..0b7b63a --- /dev/null +++ b/org.glite.lb.client-interface/interface/consumer.h @@ -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 index 0000000..1560924 --- /dev/null +++ b/org.glite.lb.client-interface/interface/context.h @@ -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 index 0000000..37f9dd7 --- /dev/null +++ b/org.glite.lb.client-interface/interface/dump.h @@ -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 index 0000000..971d916 --- /dev/null +++ b/org.glite.lb.client-interface/interface/events.h.T @@ -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 +#include + +#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 index 0000000..129b61d --- /dev/null +++ b/org.glite.lb.client-interface/interface/jobstat.h.T @@ -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 index 0000000..5ca8ead --- /dev/null +++ b/org.glite.lb.client-interface/interface/load.h @@ -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 index 0000000..b869daf --- /dev/null +++ b/org.glite.lb.client-interface/interface/notification.h @@ -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 index 0000000..3d1ed53 --- /dev/null +++ b/org.glite.lb.client-interface/interface/notifid.h @@ -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 index 0000000..b00b7d9 --- /dev/null +++ b/org.glite.lb.client-interface/interface/producer.h.T @@ -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 index 0000000..531c903 --- /dev/null +++ b/org.glite.lb.client-interface/interface/purge.h @@ -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 index 0000000..a58c826 --- /dev/null +++ b/org.glite.lb.client-interface/project/properties.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/org.glite.lb.client/.Makefile.swp b/org.glite.lb.client/.Makefile.swp new file mode 100644 index 0000000000000000000000000000000000000000..d54e7cd07b114bda36ddd43e348577ac97678bd7 GIT binary patch literal 12288 zcmeI2%WoS+9LJ{|XxtYF{())hs9e08J`yAsS4kYFb#arZcB;@?xa-}qz3q5swU4CW zF5&_lpa(9z1QO3X9FTxBl{j#K6XJCO3Guq{2N1q9yUwF-+8#Jnv&zT2-ucaU=J%bQ zS;?99#D(fPI%OOqI36bCqqn}D{;IuxAUQ|~zas3OB@6C+^xVMe8?GH1{n&F2``I1B zO{)`{Tf)qq=rc2I#dNI5>ISZ3cDWldQv_W@T^YP>*xY4aWS+CGvX0By4)Xf8c0fCD z7YB|XJ34Xr&_Vj>Bg=P*)WzBX?SOVbJD?rV4rm9o1KI)Y!2RYx9q=}I9$W;+ zz)|qeK05jmA-{lczzbjv ztb(V(W8etb4}QxLaueJDFM$`q6)*)RK_0BbqkqBQ;4knUxCy=lUw{w5``~qO0|ekA zcnX{a(_kFz2m65Zd=JQhF`y6afObGTpdHW-{ErUg>|S3uw6I3=)AW?-uvOEGIVZXN zV5V_Aq4_81sl00Mo+78|_3Jd~(?SP&B)4%jZrHwGr0Qfe@K(E*X73=Tq?Ay8P%fjJ zgYtZB=5#%w1si=PwTGqoCe)TfV6PSQj9ynhIvgAn^8S=;rJbcbYtw+pi9ByEyhM+DTTu7`Hh z3^&?1NgW|0whK?4II-D9CCuG+Lev#y*L)E~)Z)BIZPEADj!Ot^GitC@TdvoX!?Y0i zR@8&whEgU@@@ZIgR#`5qs2YSEM#QGUBjWIVXYB%48$(;X-9q>JLS}uYQd_E=S7U{` z!vW43%Q3qr=z^fwof^@Ig;7XDK|5Ap5WC0ht3^VxM2AlXBvpdY!sRWV65_Cq74xVl zO(H}U7fVju_u;$cP(Kd3*rl2&6iYC7v&(8PtM41!BIxSv3S|+=GAfn|wXMbulV9tH zt%!%MFyyTs<30<*v^|+~v2-tmUxaQXf@cylKfsK!qQt~7(*jptSW3gOMSDmyeF2^@N=yK7H!w$DrMI0oM#uSiuvI#H}fy!I7 zpgr1`R3PmQvdWen^*BaCa9SwfrNK(Bhe^#j))sY!dkasF1aa59iteYYbZstMrb)IY zkA!D)ZKFoEt`m|~z2%uF<&_mBSuAb51LQDNcNhlOvw7?=LbGEuaF}}*i4ojxuqk}z z!Fe@`BhB2c_OO;(4bcRaa%hA>cHw#v3p$p~k|s$rmwC-qW|vPVsH!gDK$jL4C-82o zFVD|cS1NT{qDBMX@yafiFx@diP~~krxE;4ES1I1cQ+frnC^4<*IJvMi(>NogRm*t4 NXW7!>HhKd$$bZ%#@Tvd+ literal 0 HcmV?d00001 diff --git a/org.glite.lb.client/Makefile b/org.glite.lb.client/Makefile new file mode 100644 index 0000000..dd811ad --- /dev/null +++ b/org.glite.lb.client/Makefile @@ -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 index 0000000..4aa4105 --- /dev/null +++ b/org.glite.lb.client/build.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.lb.client/interface/CountRef.h b/org.glite.lb.client/interface/CountRef.h new file mode 100644 index 0000000..cedf203 --- /dev/null +++ b/org.glite.lb.client/interface/CountRef.h @@ -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 +class CountRef { +public: + CountRef(void *); +// CountRef(void *,void (*)(void *)); + + void use(void); + void release(void); + + void *ptr; +private: + int count; +// void (*destroy)(void *); +}; + +template +CountRef::CountRef(void *p) +{ + ptr = p; + count = 1; +} + +template +void CountRef::release(void) +{ + if (--count == 0) { + T::destroyFlesh(ptr); + delete this; + } +} + +template +void CountRef::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 index 0000000..7aac0b4 --- /dev/null +++ b/org.glite.lb.client/interface/Event.h.T @@ -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 +#include +#include + +#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; +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 > & getAttrs(void) const; + +private: + static void destroyFlesh(void *); + CountRef *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 index 0000000..d041dfe --- /dev/null +++ b/org.glite.lb.client/interface/Job.h @@ -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 &) const; + const std::vector 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 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 index 0000000..c1519b0 --- /dev/null +++ b/org.glite.lb.client/interface/JobStatus.h.T @@ -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 +#include "edg/workload/logging/client/jobstat.h" + +#include +#include +#include + +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; +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 getValIntList(Attr) const; + + /** Retrieve string list attribute */ + const std::vector getValStringList(Attr) const; + + /** Retrieve tag list attribute */ + const std::vector > getValTagList(Attr) const; + + /** Retrieve job status list attribute */ + const std::vector getValJobStatusList(Attr) const; + + /** Attribute name */ + const std::string& getAttrName(Attr) const; + + /** List of attributes and types valid for this instance */ + const std::vector >& 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 *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 index 0000000..9f3a2c9 --- /dev/null +++ b/org.glite.lb.client/interface/LoggingExceptions.h @@ -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 + +#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 index 0000000..d7a32e7 --- /dev/null +++ b/org.glite.lb.client/interface/Notification.h @@ -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 &); + + /** 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 jobs; + std::vector 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 index 0000000..fbeb55c --- /dev/null +++ b/org.glite.lb.client/interface/ServerConnection.h @@ -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 +#include + +#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 &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 getQueryServer() const; + int getQueryTimeout() const; + + std::string getX509Proxy() const; + std::pair 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 > > + getIndexedAttrs(void); + + /** Retrieve hard and soft result set size limit */ + std::pair 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& job_cond, + const std::vector& event_cond, + std::vector&) const; + + const std::vector queryEvents(const std::vector& job_cond, + const std::vector& event_cond) const; + + const std::list queryEventsList(const std::vector& job_cond, + const std::vector& 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& job_cond, + const std::vector& 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 >& job_cond, + const std::vector >& event_cond, + std::vector&) const; + + const std::vector + queryEvents(const std::vector >& job_cond, + const std::vector >& 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& query, + std::vector& ids) const; + + const std::vector + queryJobs(const std::vector& 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 >& query, + std::vector& ids) const; + + const std::vector + queryJobs(const std::vector >& 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& query, + int flags, + std::vector & states) const; + const std::vector queryJobStates(const std::vector& query, + int flags) const; + + const std::list queryJobStatesList(const std::vector& 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 >& query, + int flags, + std::vector & states) const; + const std::vector + queryJobStates(const std::vector >& query, + int flags) const; + + /** States of all user's jobs. + * Convenience wrapper around queryJobs. + */ + void userJobStates(std::vector& stateList) const; + const std::vector userJobStates() const; + + + /** JobId's of all user's jobs. + * Convenience wrapper around queryJobs. + */ + void userJobs(std::vector &) const; + const std::vector 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 index 0000000..0d9e1e1 --- /dev/null +++ b/org.glite.lb.client/project/properties.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/org.glite.lb.client/project/taskdefs.xml b/org.glite.lb.client/project/taskdefs.xml new file mode 100755 index 0000000..9125c37 --- /dev/null +++ b/org.glite.lb.client/project/taskdefs.xml @@ -0,0 +1,4 @@ + + + + diff --git a/org.glite.lb.client/src/Event.cpp.T b/org.glite.lb.client/src/Event.cpp.T new file mode 100644 index 0000000..cf24f00 --- /dev/null +++ b/org.glite.lb.client/src/Event.cpp.T @@ -0,0 +1,337 @@ +/* +@@@AUTO +*/ + +#include +#include +#include +#include + +#include + +#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((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 tpair; +static std::vector 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 > 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 index 0000000..a37aa50 --- /dev/null +++ b/org.glite.lb.client/src/Job.cpp @@ -0,0 +1,209 @@ +#ident "$Header$" + +/** + * @file Job.cpp + * @version $Revision$ + */ + + +#include +#include +#include + +#include + +#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 &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 +Job::log(void) const +{ + std::vector eventList; + + log(eventList); + return(eventList); +} + + +const std::pair +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(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 index 0000000..6e09ac7 --- /dev/null +++ b/org.glite.lb.client/src/JobStatus.cpp.T @@ -0,0 +1,481 @@ +#include +#include +#include +#include +#include + +#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((void*)&in); +} + +JobStatus & +JobStatus::operator=(const edg_wll_JobStat & in) +{ + if(flesh) + flesh->release(); + status = (Code)in.state; + flesh = new CountRef((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((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 +JobStatus::getValIntList(Attr attr) const +{ + edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr; + + std::vector 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 +JobStatus::getValStringList(Attr attr) const +{ + edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr; + + std::vector 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 > +JobStatus::getValTagList(Attr attr) const +{ + edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr; + + std::vector > 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(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::getValJobStatusList(Attr attr) const +{ + edg_wll_JobStat const *cstat = (edg_wll_JobStat *) flesh->ptr; + + std::vector 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 tpair; +static std::vector 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& +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 index 0000000..6b73c1e --- /dev/null +++ b/org.glite.lb.client/src/Notification.cpp @@ -0,0 +1,276 @@ +#ident "$Header$" + +/** + * @file Notification.cpp + * @version $Revision$ + */ + +#include +#include +#include + +#include +#include + +#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 > &); + +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(),¬ifId); + 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,¬ifId); + 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::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::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::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 &jobStates) +{ + states = jobStates; +} + +std::string +Notification::getStates(void) +{ + std::vector::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::iterator it; + std::vector::iterator its; + std::vector > query; + edg_wll_QueryRec **conditions = NULL; + unsigned i; + + try { + /* fill in the query: */ + for( it = jobs.begin(); it != jobs.end(); it++ ) { + std::vector 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,¬ifId,&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,¬ifId); + 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 index 0000000..015cdeb --- /dev/null +++ b/org.glite.lb.client/src/ServerConnection.cpp @@ -0,0 +1,1302 @@ +//#ident "$Header$" + +/** + * @file ServerConnection.cpp + * @version $Revision$ + */ +#include +#include +#include + +#include +#include +#include + +#include + +#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 +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 +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 > > +ServerConnection::getIndexedAttrs(void) { + edg_wll_QueryRec **recs; + int i,j; + std::vector > > out; + + check_result(edg_wll_GetIndexedAttrs(context,&recs),context, + "edg_wll_GetIndexedAttrs()"); + + for (i=0; recs[i]; i++) { + std::vector > 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(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 &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 > &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& job_cond, + const std::vector& event_cond, + std::vector & 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 +ServerConnection::queryEvents(const std::vector& job_cond, + const std::vector& event_cond) const +{ + std::vector eventList; + + queryEvents(job_cond, event_cond,eventList); + return eventList; +} + +const std::list +ServerConnection::queryEventsList(const std::vector& job_cond, + const std::vector& event_cond) const +{ + std::vector events; + + queryEvents(job_cond, event_cond, events); + return std::list(events.begin(),events.end()); +} + +std::string +ServerConnection::queryEventsAggregate(const std::vector& job_cond, + const std::vector& 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 >& job_cond, + const std::vector >& event_cond, + std::vector& 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 +ServerConnection::queryEvents(const std::vector >& job_cond, + const std::vector >& event_cond) const +{ + std::vector eventList; + + queryEvents(job_cond, event_cond,eventList); + return eventList; +} + + +void ServerConnection::queryJobs(const std::vector& query, + std::vector & 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 +ServerConnection::queryJobs(const std::vector& query) const +{ + std::vector jobList; + + queryJobs(query, jobList); + return jobList; +} + + +void +ServerConnection::queryJobs(const std::vector >& query, + std::vector& 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 +ServerConnection::queryJobs(const std::vector >& query) const +{ + std::vector jobList; + + queryJobs(query, jobList); + return jobList; +} + + +void +ServerConnection::queryJobStates(const std::vector& query, + int flags, + std::vector & 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 +ServerConnection::queryJobStates(const std::vector& query, + int flags) const +{ + std::vector states; + + queryJobStates(query, flags, states); + return(states); +} + +const std::list +ServerConnection::queryJobStatesList(const std::vector& query, + int flags) const +{ + std::vector states; + + queryJobStates(query, flags, states); + return std::list(states.begin(),states.end()); +} + + +void +ServerConnection::queryJobStates(const std::vector >& query, + int flags, + std::vector & 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 +ServerConnection::queryJobStates(const std::vector >& query, + int flags) const +{ + std::vector states; + + queryJobStates(query, flags, states); + return(states); +} + + +void ServerConnection::userJobs(std::vector & 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 +ServerConnection::userJobs() const +{ + std::vector jobList; + + userJobs(jobList); + return jobList; +} + + +void +ServerConnection::userJobStates(std::vector & 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 +ServerConnection::userJobStates() const +{ + std::vector 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 index 0000000..20defdf --- /dev/null +++ b/org.glite.lb.client/src/args.c.T @@ -0,0 +1,533 @@ +/* +@@@AUTO +*/ + +@@@LANG: C + +#include "args.h" +#include "glite/lb/events.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +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 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 index 0000000..6daaf4e --- /dev/null +++ b/org.glite.lb.client/src/args.h @@ -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 index 0000000..2e09ac7 --- /dev/null +++ b/org.glite.lb.client/src/connection.c @@ -0,0 +1,263 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; iconnOpened;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; iconnOpened; 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 index 0000000..99dd933 --- /dev/null +++ b/org.glite.lb.client/src/connection.h @@ -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 index 0000000..d94dbbc --- /dev/null +++ b/org.glite.lb.client/src/consumer.c @@ -0,0 +1,429 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..e4e4542 --- /dev/null +++ b/org.glite.lb.client/src/dump.c @@ -0,0 +1,233 @@ +#ident "$Header$" + + +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..514def0 --- /dev/null +++ b/org.glite.lb.client/src/load.c @@ -0,0 +1,210 @@ +#ident "$Header$" + + +#include +#include +#include +#include +#include +#include + +#include + +#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 index 0000000..faa67c3 --- /dev/null +++ b/org.glite.lb.client/src/logevent.c.T @@ -0,0 +1,272 @@ +/* +@@@AUTO +*/ + +@@@LANG: C + +#ident "$Header$" +#include +#include +#include +#include +#include +#include // log +#include // isspace + +#include + +#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 index 0000000..6eb186a --- /dev/null +++ b/org.glite.lb.client/src/notification.c @@ -0,0 +1,743 @@ +#ident "$Header" + +#include +#include +#include +#include +#include +#include +#include + + +#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,¬ifId)) ) + 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, ¬_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, ¬_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 index 0000000..696d58d --- /dev/null +++ b/org.glite.lb.client/src/prod_proto.c @@ -0,0 +1,152 @@ +#ident "$Header$" + +#include "prod_proto.h" +#include "glite/lb/producer.h" +#include "glite/lb/escape.h" + +#include +#include +#include + +/* + *---------------------------------------------------------------------- + * + * 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 index 0000000..8b22258 --- /dev/null +++ b/org.glite.lb.client/src/prod_proto.h @@ -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 index 0000000..e21b176 --- /dev/null +++ b/org.glite.lb.client/src/producer.c @@ -0,0 +1,594 @@ +/** + * \file producer.c + * \author Jan Pospisil + */ + +#ident "$Header$" + +#include +#include + +#include +#include +#include +#include + +#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 index 0000000..7d4ba32 --- /dev/null +++ b/org.glite.lb.client/src/purge.c @@ -0,0 +1,392 @@ +#ident "$Header$" + + +#include +#include +#include +#include +#include +#include + +#include + +#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 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 index 0000000..1e01840 --- /dev/null +++ b/org.glite.lb.client/src/uiwrap.c.T @@ -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 index 0000000..99a1096 --- /dev/null +++ b/org.glite.lb.common/Makefile @@ -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 index 0000000..4aa4105 --- /dev/null +++ b/org.glite.lb.common/build.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.lb.common/interface/authz.h b/org.glite.lb.common/interface/authz.h new file mode 100644 index 0000000..ccb02ad --- /dev/null +++ b/org.glite.lb.common/interface/authz.h @@ -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 index 0000000..e7a2954 --- /dev/null +++ b/org.glite.lb.common/interface/context-int.h @@ -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 index 0000000..1de1ec4 --- /dev/null +++ b/org.glite.lb.common/interface/dgssl.h @@ -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 +#include "glite/wms/thirdparty/globus_ssl_utils/sslutils.h" + +#include +#include +#include +#include +#include +#include +#if SSLEAY_VERSION_NUMBER >= 0x0090581fL +#include +#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 index 0000000..4cf54c0 --- /dev/null +++ b/org.glite.lb.common/interface/escape.h @@ -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 index 0000000..d4ed8d7 --- /dev/null +++ b/org.glite.lb.common/interface/events_parse.h @@ -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 index 0000000..db480f7 --- /dev/null +++ b/org.glite.lb.common/interface/il_string.h @@ -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 index 0000000..a2a59a8 --- /dev/null +++ b/org.glite.lb.common/interface/log_proto.h @@ -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 index 0000000..beea6e7 --- /dev/null +++ b/org.glite.lb.common/interface/mini_http.h @@ -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 index 0000000..04f133c --- /dev/null +++ b/org.glite.lb.common/interface/trio.h @@ -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 +#include +#include + +#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 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 index 0000000..ee7d85c --- /dev/null +++ b/org.glite.lb.common/interface/ulm_parse.h @@ -0,0 +1,47 @@ +#ifndef __EDG_WORKLOAD_LOGGING_COMMON_ULM_PARSE_H__ +#define __EDG_WORKLOAD_LOGGING_COMMON_ULM_PARSE_H__ + +#ident "$Header$" + +#include /* for ULCconvertDate */ +#include + +/*========= 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 index 0000000..bb1d307 --- /dev/null +++ b/org.glite.lb.common/interface/xml_conversions.h @@ -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 index 0000000..b87d806 --- /dev/null +++ b/org.glite.lb.common/interface/xml_parse.h @@ -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 index 0000000..27df03d --- /dev/null +++ b/org.glite.lb.common/project/properties.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/org.glite.lb.common/src/context.c b/org.glite.lb.common/src/context.c new file mode 100644 index 0000000..407b852 --- /dev/null +++ b/org.glite.lb.common/src/context.c @@ -0,0 +1,419 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include + +#include +#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; iconnPool = (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; ipoolSize; 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= 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= 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 index 0000000..1e109e7 --- /dev/null +++ b/org.glite.lb.common/src/dgssl.c @@ -0,0 +1,679 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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;icert_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 index 0000000..8ace584 --- /dev/null +++ b/org.glite.lb.common/src/escape.c @@ -0,0 +1,221 @@ +#ident "$Header$" + +#include +#include +#include +#include + +#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',"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 index 0000000..2897e71 --- /dev/null +++ b/org.glite.lb.common/src/events.c.T @@ -0,0 +1,349 @@ +#ident "$Header$" +/* +@@@AUTO +*/ +@@@LANG: C + +#include +#include +#include +#include + +#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])) 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])) 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\])) 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\])) 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 index 0000000..7854152 --- /dev/null +++ b/org.glite.lb.common/src/events_parse.c.T @@ -0,0 +1,732 @@ +#ident "$Header$" +/* +@@@AUTO +*/ +@@@LANG: C + +#include +#include +#include +#include + +#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; inum; 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; inum; 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; inum; 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; inum; 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 index 0000000..7d4bdf5 --- /dev/null +++ b/org.glite.lb.common/src/il_int.c @@ -0,0 +1,69 @@ +#ident "$Header$" + +#include +#include +#include +#include + +#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 index 0000000..11170c5 --- /dev/null +++ b/org.glite.lb.common/src/il_log.c @@ -0,0 +1,32 @@ +#ident "$Header$" + +#include +#include +#include +#include + +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 index 0000000..30997f6 --- /dev/null +++ b/org.glite.lb.common/src/il_msg.c @@ -0,0 +1,53 @@ +#ident "$Header$" + +#include "il_string.h" + +#include +#include +#include + +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 index 0000000..2c4034d --- /dev/null +++ b/org.glite.lb.common/src/il_string.c @@ -0,0 +1,61 @@ +#ident "$Header$" + +#include +#include +#include + +#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 index 0000000..722e246 --- /dev/null +++ b/org.glite.lb.common/src/mini_http.c @@ -0,0 +1,224 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..0904325 --- /dev/null +++ b/org.glite.lb.common/src/notifid.c @@ -0,0 +1,88 @@ +#include +#include +#include + +#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 index 0000000..fc52d01 --- /dev/null +++ b/org.glite.lb.common/src/notifid.h @@ -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 index 0000000..479f654 --- /dev/null +++ b/org.glite.lb.common/src/param.c @@ -0,0 +1,429 @@ +#include +#include +#include +#include +#include + +#include + +#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; ip_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 index 0000000..c57b8f6 --- /dev/null +++ b/org.glite.lb.common/src/query_rec.c @@ -0,0 +1,50 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include + +#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 index 0000000..301c96b --- /dev/null +++ b/org.glite.lb.common/src/status.c.T @@ -0,0 +1,172 @@ +#ident "$Header$" + +#include +#include + +#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])) 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 index 0000000..f1ab5b8 --- /dev/null +++ b/org.glite.lb.common/src/strio.c @@ -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 +#include +#include +#include +#include +#include +#ifndef DEBUG +# define NDEBUG +#endif +#include + +#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 ::= [ ] + * ( | + * | + * ) + * [ [ ] ] + * number ::= 1*( ) + * 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 index 0000000..68845a3 --- /dev/null +++ b/org.glite.lb.common/src/strio.h @@ -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 +#include +#include +#include + +#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 index 0000000..d46736e --- /dev/null +++ b/org.glite.lb.common/src/trio.c @@ -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 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 +#include +#if !defined(TRIO_COMPILER_SUPPORTS_C99) && !defined(isblank) +# define isblank(x) (((x)==32) || ((x)==9)) +#endif +#include +#include +#include +#include +#include +#include + +#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 +# include +# include +# define USE_LOCALE +#endif /* PLATFORM_UNIX */ +#if defined(PLATFORM_WIN32) +# include +# define read _read +# define write _write +#endif /* PLATFORM_WIN32 */ + +#if TRIO_WIDECHAR +# if defined(TRIO_COMPILER_SUPPORTS_ISO94) +# include +# include +# 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 +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 +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 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 = ¶meters[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 = ¶meters[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 index 0000000..ca49fab --- /dev/null +++ b/org.glite.lb.common/src/triop.h @@ -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 + +#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 index 0000000..1a40d57 --- /dev/null +++ b/org.glite.lb.common/src/ulm_parse.c @@ -0,0 +1,410 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include + +/*========= 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; iraw); + + /* 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; iraw[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; iraw[iArrayEQ[i]] = '\0'; + for (i=0; iraw[iArraySP[i]] = '\0'; + + this->num = eqCnt; + + /* Debug dump of parsed fields */ +// for( i=0; iraw+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 index 0000000..89ad510 --- /dev/null +++ b/org.glite.lb.common/src/xml_conversions.c @@ -0,0 +1,835 @@ +#ident "$Header$" + +#include +#include + +#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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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= 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])) 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])) 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])) 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 index 0000000..2cf477c --- /dev/null +++ b/org.glite.lb.common/src/xml_parse.c.T @@ -0,0 +1,2613 @@ +#ident "$Header$" + +#include +#include +#include +#include + +#include // 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 "\r\n" +#define QUERY_EVENTS_OREC_BEGIN "\t\t\r\n" +#define QUERY_EVENTS_OREC_END "\t\t\r\n" +#define QUERY_EVENTS_ORJC_BEGIN "\t\t\r\n" +#define QUERY_EVENTS_ORJC_END "\t\t\r\n" +#define QUERY_JOBS_REQUEST_BEGIN "\r\n" +#define QUERY_JOBS_OR_BEGIN "\t\t\r\n" +#define QUERY_JOBS_OR_END "\t\t\r\n" +#define PURGE_REQUEST_BEGIN "\r\n" +#define PURGE_REQUEST_END "\r\n" +#define DUMP_REQUEST_BEGIN "\r\n" +#define DUMP_REQUEST_END "\r\n" +#define LOAD_REQUEST_BEGIN "\r\n" +#define LOAD_REQUEST_END "\r\n" +#define INDEXED_ATTRS_REQUEST_BEGIN "\r\n" +#define INDEXED_ATTRS_REQUEST_END "\r\n" +#define NOTIF_REQUEST_BEGIN "\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; ichar_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; ierrDesc = 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; ierrDesc = 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\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\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\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%s\r\n\t\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%|Xs\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\r\n%s\t\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 index 0000000..b3a55bd --- /dev/null +++ b/org.glite.lb.server/Makefile @@ -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 index 0000000..4aa4105 --- /dev/null +++ b/org.glite.lb.server/build.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.lb.server/project/properties.xml b/org.glite.lb.server/project/properties.xml new file mode 100755 index 0000000..0ca2172 --- /dev/null +++ b/org.glite.lb.server/project/properties.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/org.glite.lb.server/src/bkindex.c b/org.glite.lb.server/src/bkindex.c new file mode 100644 index 0000000..3c230ee --- /dev/null +++ b/org.glite.lb.server/src/bkindex.c @@ -0,0 +1,305 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include + +#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= 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; iattr == 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 [file]\n" + " -m,--mysql 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 index 0000000..1d22296 --- /dev/null +++ b/org.glite.lb.server/src/bkserverd.c @@ -0,0 +1,1308 @@ +#ident "$Header$" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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= 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; ip_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_dispatchedmysql; + 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= 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 index 0000000..6ff49cc --- /dev/null +++ b/org.glite.lb.server/src/db_store.c @@ -0,0 +1,91 @@ +#ident "$Header$" + +#include +#include +#include +#include + +#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 index 0000000..d3c5dd3 --- /dev/null +++ b/org.glite.lb.server/src/dump.c @@ -0,0 +1,192 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include + +#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; ifrom = 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 index 0000000..a4b2f6e --- /dev/null +++ b/org.glite.lb.server/src/get_events.c.T @@ -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 +#include +#include +#include +#include +#include + +#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 index 0000000..590b61e --- /dev/null +++ b/org.glite.lb.server/src/get_events.h @@ -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 index 0000000..5ac8c53 --- /dev/null +++ b/org.glite.lb.server/src/il_notification.c @@ -0,0 +1,388 @@ +/** + * il_notification.c + * - implementation of IL API calls for notifications + * + */ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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, + ®_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 index 0000000..3c842c9 --- /dev/null +++ b/org.glite.lb.server/src/il_notification.h @@ -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 index 0000000..b3cbd18 --- /dev/null +++ b/org.glite.lb.server/src/index.c.T @@ -0,0 +1,227 @@ +#include +#include +#include +#include + +#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= 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; iattr != 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 index 0000000..2e585f2 --- /dev/null +++ b/org.glite.lb.server/src/index.h @@ -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 index 0000000..fa552a1 --- /dev/null +++ b/org.glite.lb.server/src/index_lex.l @@ -0,0 +1,47 @@ +%{ +#ident "$Header$" + +#include + +#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 index 0000000..4c9265b --- /dev/null +++ b/org.glite.lb.server/src/index_parse.y @@ -0,0 +1,242 @@ +%{ +#ident "$Header$" + + +#include +#include + +#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 string +%type int +%type job_index_elem +%type job_index job_index_elem_list +%type job_index_list job_indices; +%type 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 index 0000000..6d2e64f --- /dev/null +++ b/org.glite.lb.server/src/jobstat.c @@ -0,0 +1,1250 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..1b17c7b --- /dev/null +++ b/org.glite.lb.server/src/jobstat.h @@ -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 index 0000000..5a6f97e --- /dev/null +++ b/org.glite.lb.server/src/jobstat_supp.c @@ -0,0 +1,669 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..3f4c39b --- /dev/null +++ b/org.glite.lb.server/src/lb_authz.c @@ -0,0 +1,765 @@ +#ident "$Header$" + +#include +#include +#include +#include + +#include +#include +#include +#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 index 0000000..43ffc1a --- /dev/null +++ b/org.glite.lb.server/src/lb_authz.h @@ -0,0 +1,42 @@ +#ifndef LB_AUTHZ_H +#define LB_AUTHZ_H + +#include +#include + +#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 index 0000000..e91a3ba --- /dev/null +++ b/org.glite.lb.server/src/lb_html.c @@ -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 +#include +#include + +#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
  • %s\r\n", + pomB, chid,chid); + + free(chid); + free(pomB); + pomB = pomA; + i++; + } + + asprintf(&pomA, "\r\n\t\r\n" + "

    User jobs

    \r\n" + "User subject: %s

    " + "

      %s
    " + "\t\r\n",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" name ":" \ + "" type "",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,"

    Job description

    \r\n" + "
    %s
    \r\n",stat.jdl); + + if (stat.rsl) asprintf(&rsl,"

    RSL

    \r\n" + "
    %s
    \r\n",stat.rsl); + + + asprintf(&pomA, "\r\n\t\r\n" + "

    %s

    \r\n" + "%s
    " + "%s%s" + "\t\r\n", + 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,"Error\n" + "

    %s

    \n" + "%d: %s (%s)",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 index 0000000..1cac83f --- /dev/null +++ b/org.glite.lb.server/src/lb_html.h @@ -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 index 0000000..79e85d5 --- /dev/null +++ b/org.glite.lb.server/src/lb_http.c @@ -0,0 +1,53 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include + +#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 index 0000000..c3e012d --- /dev/null +++ b/org.glite.lb.server/src/lb_http.h @@ -0,0 +1,12 @@ +#ifndef _LB_HTTP_H +#define _LB_HTTP_H + +#ident "$Header$" + +#include + +#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 index 0000000..240f795 --- /dev/null +++ b/org.glite.lb.server/src/lb_proto.c @@ -0,0 +1,565 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include + +#include + +#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, ¬ifId, + &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 index 0000000..aea3957 --- /dev/null +++ b/org.glite.lb.server/src/lb_proto.h @@ -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 index 0000000..92fcefd --- /dev/null +++ b/org.glite.lb.server/src/lb_xml_parse.c.T @@ -0,0 +1,1731 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include + +#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 "\r\n" +#define QUERY_JOBS_BEGIN "\r\n" +#define PURGE_RESULT_BEGIN "\r\n" +#define DUMP_RESULT_BEGIN "\r\n" +#define LOAD_RESULT_BEGIN "\r\n" +#define INDEXED_ATTRS_BEGIN "\r\n" +#define NOTIF_RESULT_BEGIN "\r\n" + + + +// XXX will be redundant soon +#define USERJOBS_BEGIN "\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; ijob_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; ijob_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; itype == 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; itype == 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; ichar_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," \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 \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\r\n"); + pomB = pomA; + + if (jobsIn) { + trio_asprintf(&pomA,"%s\t\t%|Xs\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\r\n\r\n\t\t\r\n", + pomB, pomC); + } + + free(pomB); + free(pomC); + pomB = pomA; + + + len = asprintf(&pomA,"%s\t\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," %|Xs\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\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,"\r\n%s", + pomC, ctx->errCode,ctx->errDesc, pomB); + else + trio_asprintf(&pomA,"\r\n%s", + 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,""); + 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,""); + 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,""); + free(pomA); + pomA = pomB; + } + + asprintf(&pomB, "%s\t%s\r\n",pomA,""); + 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 index 0000000..51db4cf --- /dev/null +++ b/org.glite.lb.server/src/lb_xml_parse.h @@ -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 index 0000000..53809d2 --- /dev/null +++ b/org.glite.lb.server/src/lbs_db.c @@ -0,0 +1,219 @@ +#ident "$Header$" + +#include "mysql/mysql.h" // MySql header file +#include "mysql/mysqld_error.h" +#include "mysql/errmsg.h" + +#include +#include +#include +#include +#include +#include + +#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; iresult))) 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 index 0000000..3d7a344 --- /dev/null +++ b/org.glite.lb.server/src/lbs_db.h @@ -0,0 +1,85 @@ +#ifndef _LBS_DB_H +#define _LBS_DB_H + +#ident "$Header$" + +#include +#include + +#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 index 0000000..0f08964 --- /dev/null +++ b/org.glite.lb.server/src/load.c @@ -0,0 +1,210 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..a11e27c --- /dev/null +++ b/org.glite.lb.server/src/lock.c @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +#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; nsemaphores,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 index 0000000..c67ec6d --- /dev/null +++ b/org.glite.lb.server/src/lock.h @@ -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 index 0000000..d50de60 --- /dev/null +++ b/org.glite.lb.server/src/notif_match.c @@ -0,0 +1,166 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include + +#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; ip_instance = strdup(""); + if ( edg_wll_NotifJobStatus(ctx, nid, dest, port, jobc[3], *stat) ) + { + free(dest); + for (i=0; inoAuth || 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 index 0000000..2ddbdaa --- /dev/null +++ b/org.glite.lb.server/src/notification.c @@ -0,0 +1,436 @@ +#include +#include +#include +#include + +#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', '%|Ss')", + 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 index 0000000..5b34974 --- /dev/null +++ b/org.glite.lb.server/src/openserver.c @@ -0,0 +1,18 @@ +#ident "$Header$" + +#include + +#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 index 0000000..8841e12 --- /dev/null +++ b/org.glite.lb.server/src/purge.h @@ -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 index 0000000..a4da45a --- /dev/null +++ b/org.glite.lb.server/src/query.c @@ -0,0 +1,1414 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include + +#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 index 0000000..fb421e4 --- /dev/null +++ b/org.glite.lb.server/src/query.h @@ -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 index 0000000..0424043 --- /dev/null +++ b/org.glite.lb.server/src/request.c @@ -0,0 +1,96 @@ +#ident "$Header$" + +#include +#include +#include + +#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 index 0000000..d2bf8f0 --- /dev/null +++ b/org.glite.lb.server/src/server_state.c @@ -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 index 0000000..029fb7c --- /dev/null +++ b/org.glite.lb.server/src/server_state.h @@ -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 index 0000000..822d9c5 --- /dev/null +++ b/org.glite.lb.server/src/srv_purge.c @@ -0,0 +1,561 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; itimeout[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; istrict_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 index 0000000..c71e33b --- /dev/null +++ b/org.glite.lb.server/src/store.c.T @@ -0,0 +1,566 @@ +#ident "$Header$" + +/* + +@@@AUTO + + * XXX: still lots of hardcoded stuff + * there's mapping db.column <-> event struct field + */ + +@@@LANG: C + +#include +#include +#include +#include +#include +#include + +#include + +#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($idxany.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; insubjobs; 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 index 0000000..ff62047 --- /dev/null +++ b/org.glite.lb.server/src/store.h @@ -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 index 0000000..87e1b3d --- /dev/null +++ b/org.glite.lb.server/src/stored_master.c @@ -0,0 +1,58 @@ +#ident "$Header$" + +#include +#include +#include +#include +#include +#include +#include + +#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 index 0000000..a3b2ca4 --- /dev/null +++ b/org.glite.lb.server/src/userjobs.c @@ -0,0 +1,77 @@ +#ident "$Header$" + +#include +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 index 0000000..93faca8 --- /dev/null +++ b/org.glite.lb/build.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.glite.lb/project/MultiStruct.pm b/org.glite.lb/project/MultiStruct.pm new file mode 100644 index 0000000..9cd847c --- /dev/null +++ b/org.glite.lb/project/MultiStruct.pm @@ -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 index 0000000..95d33b8 --- /dev/null +++ b/org.glite.lb/project/StructField.pm @@ -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 index 0000000..8ff52ec --- /dev/null +++ b/org.glite.lb/project/at3 @@ -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 index 0000000..dda0b85 --- /dev/null +++ b/org.glite.lb/project/dependencies.properties @@ -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 index 0000000..b8e5399 --- /dev/null +++ b/org.glite.lb/project/events.T @@ -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 index 0000000..1ad8900 --- /dev/null +++ b/org.glite.lb/project/properties.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/org.glite.lb/project/status.T b/org.glite.lb/project/status.T new file mode 100644 index 0000000..10071ac --- /dev/null +++ b/org.glite.lb/project/status.T @@ -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 index 0000000..1f92bef --- /dev/null +++ b/org.glite.lb/project/taskdefs.xml @@ -0,0 +1,34 @@ + + + + + + + + +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} + + + + + + diff --git a/org.glite.lb/project/types.T b/org.glite.lb/project/types.T new file mode 100644 index 0000000..c4a4f7f --- /dev/null +++ b/org.glite.lb/project/types.T @@ -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"', + strlist=>'"std::vector"', + taglist=>'"std::vector>"', + stslist=>'"std::vector"', + 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"', + } +); -- 1.8.2.3