This commit was manufactured by cvs2svn to create tag 'glite-lb_R_1_10_0_2'. glite-lb_R_1_10_0_2
authorcvs2svn <admin@example.com>
Thu, 19 Aug 2010 14:30:11 +0000 (14:30 +0000)
committercvs2svn <admin@example.com>
Thu, 19 Aug 2010 14:30:11 +0000 (14:30 +0000)
Sprout from branch_RC31_3 2010-08-19 14:30:10 UTC Zdeněk Šustr <sustr4@cesnet.cz> 'New age for the subsys'
Cherrypick from master 2010-06-25 09:08:51 UTC Michal Voců <michal@ruk.cuni.cz> 'plugin framework fixes':
    org.glite.lb.harvester/doc/INSTALL
    org.glite.lb.harvester/doc/README
    org.glite.lb.harvester/doc/glite-lb-harvester.sgml
    org.glite.lb.harvester/examples/test.sql
    org.glite.lb.harvester/project/package.summary
    org.glite.lb.logger/Makefile
    org.glite.lb.logger/config/startup
    org.glite.lb.logger/configure
    org.glite.lb.logger/doc/glite-lb-interlogd.8
    org.glite.lb.logger/doc/glite-lb-logd.8
    org.glite.lb.logger/interface/il_error.h
    org.glite.lb.logger/interface/interlogd.h
    org.glite.lb.logger/project/ChangeLog
    org.glite.lb.logger/project/package.description
    org.glite.lb.logger/project/package.summary
    org.glite.lb.logger/project/version.properties
    org.glite.lb.logger/src-nt/Connection.H
    org.glite.lb.logger/src-nt/Connection.cpp
    org.glite.lb.logger/src-nt/EventManager.H
    org.glite.lb.logger/src-nt/EventManager.cpp
    org.glite.lb.logger/src-nt/Exception.H
    org.glite.lb.logger/src-nt/HTTPTransport.H
    org.glite.lb.logger/src-nt/HTTPTransport.cpp
    org.glite.lb.logger/src-nt/InputChannel.H
    org.glite.lb.logger/src-nt/InputChannel.cpp
    org.glite.lb.logger/src-nt/Makefile
    org.glite.lb.logger/src-nt/Message.H
    org.glite.lb.logger/src-nt/MessageStore.H
    org.glite.lb.logger/src-nt/MessageStore.cpp
    org.glite.lb.logger/src-nt/PlainConnection.H
    org.glite.lb.logger/src-nt/PlainConnection.cpp
    org.glite.lb.logger/src-nt/PluginManager.H
    org.glite.lb.logger/src-nt/PluginManager.cpp
    org.glite.lb.logger/src-nt/Properties.H
    org.glite.lb.logger/src-nt/Singleton.H
    org.glite.lb.logger/src-nt/SocketInput.H
    org.glite.lb.logger/src-nt/SocketInput.cpp
    org.glite.lb.logger/src-nt/ThreadPool.H
    org.glite.lb.logger/src-nt/ThreadPool.cpp
    org.glite.lb.logger/src-nt/Transport.H
    org.glite.lb.logger/src-nt/Transport.cpp
    org.glite.lb.logger/src-nt/main.cpp
    org.glite.lb.logger/src-nt/test/EventManagerTest.cpp
    org.glite.lb.logger/src-nt/test/PluginManagerTest.cpp
    org.glite.lb.logger/src-nt/test/SingletonTest.cpp
    org.glite.lb.logger/src-nt/test/ThreadPoolTest.cpp
    org.glite.lb.logger/src-nt/test/test_main.cpp
    org.glite.lb.logger/src/activemq_cpp_plugin.cpp
    org.glite.lb.logger/src/event_queue.c
    org.glite.lb.logger/src/event_store.c
    org.glite.lb.logger/src/event_store_http.c
    org.glite.lb.logger/src/http.c
    org.glite.lb.logger/src/il_error.c
    org.glite.lb.logger/src/il_master.c
    org.glite.lb.logger/src/input_queue_socket.c
    org.glite.lb.logger/src/input_queue_socket_http.c
    org.glite.lb.logger/src/interlogd.c
    org.glite.lb.logger/src/logd.c
    org.glite.lb.logger/src/logd_proto.c
    org.glite.lb.logger/src/logd_proto.h
    org.glite.lb.logger/src/perftest_il.sh
    org.glite.lb.logger/src/perftest_ll.sh
    org.glite.lb.logger/src/plugin_mgr.c
    org.glite.lb.logger/src/queue_mgr.c
    org.glite.lb.logger/src/queue_mgr_http.c
    org.glite.lb.logger/src/queue_thread.c
    org.glite.lb.logger/src/recover.c
    org.glite.lb.logger/src/send_event.c
    org.glite.lb.logger/src/send_event_http.c
    org.glite.lb.logger/src/server_msg.c
    org.glite.lb.logger/src/server_msg_http.c
    org.glite.lb.logger/test/IlTestBase.cpp
    org.glite.lb.logger/test/IlTestBase.h
    org.glite.lb.logger/test/event_queueTest.cpp
    org.glite.lb.logger/test/event_storeTest.cpp
    org.glite.lb.logger/test/il_test.cpp
    org.glite.lb.logger/test/input_queue_socketTest.cpp
    org.glite.lb.logger/test/ll_test.cpp
    org.glite.lb.logger/test/logd_proto_test.c
    org.glite.lb.logger/test/server_msgTest.cpp
Cherrypick from master 2010-03-10 12:17:51 UTC Zdeněk Šustr <sustr4@cesnet.cz> 'The most recent version copied. Do not modify this instance (RW in ./org.glite.lb).':
    org.glite.lb.harvester/Makefile
    org.glite.lb.harvester/configure
    org.glite.lb.harvester/examples/test.sh
    org.glite.lb.harvester/project/ChangeLog
    org.glite.lb.harvester/project/package.description
    org.glite.lb.harvester/project/version.properties
    org.glite.lb.harvester/src/harvester.c
Delete:
    org.glite.lb.logger/src/il_error.h
    org.glite.lb.logger/src/interlogd.h

88 files changed:
org.glite.lb.harvester/Makefile [new file with mode: 0644]
org.glite.lb.harvester/configure [new file with mode: 0755]
org.glite.lb.harvester/doc/INSTALL [new file with mode: 0644]
org.glite.lb.harvester/doc/README [new file with mode: 0644]
org.glite.lb.harvester/doc/glite-lb-harvester.sgml [new file with mode: 0644]
org.glite.lb.harvester/examples/test.sh [new file with mode: 0755]
org.glite.lb.harvester/examples/test.sql [new file with mode: 0644]
org.glite.lb.harvester/project/ChangeLog [new file with mode: 0644]
org.glite.lb.harvester/project/package.description [new file with mode: 0644]
org.glite.lb.harvester/project/package.summary [new file with mode: 0644]
org.glite.lb.harvester/project/version.properties [new file with mode: 0644]
org.glite.lb.harvester/src/harvester.c [new file with mode: 0644]
org.glite.lb.logger/Makefile
org.glite.lb.logger/config/startup
org.glite.lb.logger/configure [new file with mode: 0755]
org.glite.lb.logger/doc/glite-lb-interlogd.8
org.glite.lb.logger/doc/glite-lb-logd.8
org.glite.lb.logger/interface/il_error.h [new file with mode: 0644]
org.glite.lb.logger/interface/interlogd.h [moved from org.glite.lb.logger/src/interlogd.h with 67% similarity]
org.glite.lb.logger/project/ChangeLog
org.glite.lb.logger/project/package.description [new file with mode: 0644]
org.glite.lb.logger/project/package.summary [new file with mode: 0644]
org.glite.lb.logger/project/version.properties
org.glite.lb.logger/src-nt/Connection.H
org.glite.lb.logger/src-nt/Connection.cpp [new file with mode: 0644]
org.glite.lb.logger/src-nt/EventManager.H
org.glite.lb.logger/src-nt/EventManager.cpp [new file with mode: 0644]
org.glite.lb.logger/src-nt/Exception.H
org.glite.lb.logger/src-nt/HTTPTransport.H
org.glite.lb.logger/src-nt/HTTPTransport.cpp
org.glite.lb.logger/src-nt/InputChannel.H [new file with mode: 0644]
org.glite.lb.logger/src-nt/InputChannel.cpp [new file with mode: 0644]
org.glite.lb.logger/src-nt/Makefile
org.glite.lb.logger/src-nt/Message.H [new file with mode: 0644]
org.glite.lb.logger/src-nt/MessageStore.H [new file with mode: 0644]
org.glite.lb.logger/src-nt/MessageStore.cpp [new file with mode: 0644]
org.glite.lb.logger/src-nt/PlainConnection.H
org.glite.lb.logger/src-nt/PlainConnection.cpp
org.glite.lb.logger/src-nt/PluginManager.H
org.glite.lb.logger/src-nt/PluginManager.cpp
org.glite.lb.logger/src-nt/Properties.H [new file with mode: 0644]
org.glite.lb.logger/src-nt/Singleton.H
org.glite.lb.logger/src-nt/SocketInput.H
org.glite.lb.logger/src-nt/SocketInput.cpp
org.glite.lb.logger/src-nt/ThreadPool.H
org.glite.lb.logger/src-nt/ThreadPool.cpp
org.glite.lb.logger/src-nt/Transport.H
org.glite.lb.logger/src-nt/Transport.cpp [new file with mode: 0644]
org.glite.lb.logger/src-nt/main.cpp
org.glite.lb.logger/src-nt/test/EventManagerTest.cpp
org.glite.lb.logger/src-nt/test/PluginManagerTest.cpp
org.glite.lb.logger/src-nt/test/SingletonTest.cpp
org.glite.lb.logger/src-nt/test/ThreadPoolTest.cpp
org.glite.lb.logger/src-nt/test/test_main.cpp
org.glite.lb.logger/src/activemq_cpp_plugin.cpp [new file with mode: 0644]
org.glite.lb.logger/src/event_queue.c
org.glite.lb.logger/src/event_store.c
org.glite.lb.logger/src/event_store_http.c [new file with mode: 0644]
org.glite.lb.logger/src/http.c [new file with mode: 0644]
org.glite.lb.logger/src/il_error.c
org.glite.lb.logger/src/il_error.h [deleted file]
org.glite.lb.logger/src/il_master.c
org.glite.lb.logger/src/input_queue_socket.c
org.glite.lb.logger/src/input_queue_socket_http.c [new file with mode: 0644]
org.glite.lb.logger/src/interlogd.c
org.glite.lb.logger/src/logd.c
org.glite.lb.logger/src/logd_proto.c
org.glite.lb.logger/src/logd_proto.h
org.glite.lb.logger/src/perftest_il.sh
org.glite.lb.logger/src/perftest_ll.sh
org.glite.lb.logger/src/plugin_mgr.c [new file with mode: 0644]
org.glite.lb.logger/src/queue_mgr.c
org.glite.lb.logger/src/queue_mgr_http.c [new file with mode: 0644]
org.glite.lb.logger/src/queue_thread.c
org.glite.lb.logger/src/recover.c
org.glite.lb.logger/src/send_event.c
org.glite.lb.logger/src/send_event_http.c [new file with mode: 0644]
org.glite.lb.logger/src/server_msg.c
org.glite.lb.logger/src/server_msg_http.c [new file with mode: 0644]
org.glite.lb.logger/test/IlTestBase.cpp
org.glite.lb.logger/test/IlTestBase.h
org.glite.lb.logger/test/event_queueTest.cpp
org.glite.lb.logger/test/event_storeTest.cpp
org.glite.lb.logger/test/il_test.cpp
org.glite.lb.logger/test/input_queue_socketTest.cpp
org.glite.lb.logger/test/ll_test.cpp
org.glite.lb.logger/test/logd_proto_test.c
org.glite.lb.logger/test/server_msgTest.cpp

diff --git a/org.glite.lb.harvester/Makefile b/org.glite.lb.harvester/Makefile
new file mode 100644 (file)
index 0000000..15f0b92
--- /dev/null
@@ -0,0 +1,89 @@
+top_srcdir=..
+stagedir=.
+package=glite-lb-harvester
+module.version=0.0.0
+PREFIX=/opt/glite
+globus_prefix=/opt/globus
+
+archlib:=lib
+thrflavour:=gcc32dbgpthr
+host_cpu:=${shell uname -m}
+ifeq (${host_cpu},x86_64) 
+        archlib:=lib64
+       thrflavour:=gcc64dbgpthr
+endif   
+
+-include Makefile.inc
+-include ../project/version.properties
+version:=${module.version}
+
+CC=gcc
+VPATH=${top_srcdir}/src:${top_srcdir}/doc
+
+GLOBUS_CPPFLAGS:=-I${globus_prefix}/include/${thrflavour}
+CPPFLAGS:=-I${stagedir}/include -D_GNU_SOURCE -D_REENTRANT ${CPPFLAGS}
+CFLAGS:=-W -Wall -g -O2 ${CFLAGS}
+LDFLAGS:=${LDFLAGS} 
+LIBS:=-L${stagedir}/${archlib} -L${stagedir}/lib \
+       -lglite_lb_common_${thrflavour} \
+       -lglite_lb_client_${thrflavour} \
+       -lpthread -lglite_security_gss_${thrflavour}
+
+ifneq ($(GLITE_LB_HARVESTER_WITH_LBU_DB),no)
+CPPFLAGS:=$(CPPFLAGS) -DWITH_LBU_DB=1
+LIBS:=$(LIBS) -lglite_lbu_db
+endif
+ifeq ($(GLITE_LB_HARVESTER_WITH_OLD_LB),yes)
+CPPFLAGS:=${GLOBUS_CPPFLAGS} $(CPPFLAGS) -DWITH_OLD_LB=1
+LIBS:=$(LIBS) -lglite_wmsutils_cjobid
+else
+LIBS:=$(LIBS) -lglite_jobid -lglite_lbu_trio
+endif
+
+COMPILE:=libtool --mode=compile ${CC} ${CPPFLAGS} ${CFLAGS}
+LINK:=libtool --mode=link ${CC} ${LDFLAGS}
+INSTALL:=libtool --mode=install install
+
+default: all
+
+compile all: harvester doc debug
+
+check:
+
+debug: harvester-dbg
+
+doc: glite-lb-harvester.8
+
+stage: compile
+       $(MAKE) install PREFIX=${stagedir}
+
+install: compile
+       -mkdir -p ${PREFIX}/bin ${PREFIX}/examples ${PREFIX}/share/doc/${package}-${version} ${PREFIX}/share/man/man8
+       ${INSTALL} -m 755 harvester ${PREFIX}/bin/glite-lb-harvester
+       ${INSTALL} -m 755 harvester-dbg ${PREFIX}/examples/glite-lb-harvester-dbg
+       ${INSTALL} -m 755 ../examples/test.sh ${PREFIX}/examples/glite-lb-harvester-test.sh
+       ${INSTALL} -m 444 ../doc/README ${PREFIX}/share/doc/${package}-${version}
+       ${INSTALL} -m 444 glite-lb-harvester.8 ${PREFIX}/share/man/man8
+
+clean:
+       rm -rfv *.o *.lo *.loT .libs/ manpage.links manpage.refs
+       rm -rvf harvester harvester-dbg glite-lb-harvester.*
+       rm -rvf log.xml project/ rpmbuild/ RPMS/ tgz/
+
+harvester: harvester.o
+       ${LINK} -o $@ $+ ${LIBS}
+
+harvester-dbg: harvester-dbg.o
+       ${LINK} -o $@ $+ ${LIBS}
+
+harvester-dbg.o: harvester.c
+       ${COMPILE} -DLOG=1 -DWITH_RTM_SQL_STORAGE=1 -c $< -o $@
+
+%.o: %.c
+       ${COMPILE} -c $<
+
+%.8: %.sgml
+       docbook2man $<
+       mv $(@:.8=.1) $@
+
+.PHONY: default all compile debug check doc stage install clean
diff --git a/org.glite.lb.harvester/configure b/org.glite.lb.harvester/configure
new file mode 100755 (executable)
index 0000000..4ee7639
--- /dev/null
@@ -0,0 +1,789 @@
+#!/usr/bin/perl
+
+# WARNING: Don't edit this file unless it is the master copy in org.glite.lb
+#
+# For the purpose of standalone builds of lb/jobid/lbjp-common components
+# it is copied on tagging 
+
+# $Header$
+
+use Getopt::Long;
+
+my $pwd = `pwd`; chomp $pwd;
+my $prefix = $pwd.'/stage';
+my $stagedir;
+my $staged;
+my $module;
+my $thrflavour = 'gcc64dbgpthr';
+my $nothrflavour = 'gcc64dbg';
+my $mode = 'build';
+my $help = 0;
+my $listmodules;
+my $version;
+my $output;
+my $lb_tag = '';
+my $lbjp_tag = '';
+my $jp_tag = '';
+my $sec_tag = '';
+my $jobid_tag = '';
+my $libdir = getlibdir();
+
+my @nodes = qw/client server logger utils client-java doc ws-test db jpprimary jpindex jpclient harvester/;
+my %enable_nodes;
+my %disable_nodes;
+
+my %extern_prefix = (
+       cares => '/opt/c-ares',
+       classads => '/opt/classads',
+       cppunit => '/usr',
+       expat => '/usr',
+       globus => '/opt/globus',
+       gsoap => '/usr',
+       mysql => '/usr',
+       'mysql-devel' => '',
+       'mysql-server' => '',
+       voms => '/opt/glite',
+       gridsite => '/opt/glite',
+       lcas => '/opt/glite',
+       trustmanager => '/opt/glite',
+       ant => '/usr',
+       jdk => '/usr',
+       libtar => '/usr',
+       axis => '/usr',
+       log4c => '/usr',
+       postgresql => '/usr'
+);
+
+my %jar = (
+       'commons-codec' => '/usr/share/java/commons-codec.jar',
+       'commons-lang' => '/usr/share/java/commons-lang.jar',
+);
+
+
+my %glite_prefix;
+my %need_externs;
+my %need_externs_type;
+my %need_jars;
+my %extrafull;
+my %extranodmod;
+my %deps;
+my %deps_type;
+my %topbuild;
+
+my %lbmodules = (
+       'lb' => [ qw/client client-java common doc logger server state-machine types utils ws-interface ws-test harvester/], 
+       'security' => [qw/gss gsoap-plugin/],
+       'lbjp-common' => [qw/db log maildir server-bones trio jp-interface/],
+       'jobid' => [qw/api-c api-cpp api-java/],
+       'jp' => [ qw/client doc index primary server-common ws-interface/ ],
+       );
+
+
+my @opts = (
+       'prefix=s' => \$prefix,
+       'staged=s' => \$staged,
+       'module=s' => \$module,
+       'thrflavour=s' => \$thrflavour,
+       'nothrflavour=s' => \$nothrflavour,
+       'mode=s' => \$mode,
+       'listmodules=s' => \$listmodules,
+       'version=s' => \$version,
+       'output=s' => \$output,
+       'stage=s' => \$stagedir,
+       'lb-tag=s' => \$lb_tag,
+       'lbjp-common-tag=s' => \$lbjp_tag,
+       'jp-tag=s' => \$jp_tag,
+       'security-tag=s' => \$sec_tag,
+       'jobid-tag=s' => \$jobid_tag,
+       'help' => \$help,
+       'libdir=s' => \$libdir,
+);
+
+for (@nodes) {
+       $enable_nodes{$_} = 0;
+       $disable_nodes{$_} = 0;
+       
+       push @opts,"disable-$_",\$disable_nodes{$_};
+       push @opts,"enable-$_",\$enable_nodes{$_};
+}
+
+push @opts,"with-$_=s",\$extern_prefix{$_} for keys %extern_prefix;
+push @opts,"with-$_=s",\$jar{$_} for keys %jar;
+
+my @keeparg = @ARGV;
+
+GetOptions @opts or die "Errors parsing command line\n";
+
+$extern_prefix{'mysql-devel'}=$extern_prefix{mysql} if $extern_prefix{'mysql-devel'} eq '';
+$extern_prefix{'mysql-server'}=$extern_prefix{mysql} if $extern_prefix{'mysql-server'} eq '';
+
+if ($help) { usage(); exit 0; }
+
+if ($listmodules) {
+       my @m = map "org.glite.$listmodules.$_",@{$lbmodules{$listmodules}};
+       print "@m\n";
+       exit 0;
+}
+
+warn "$0: --version and --output make sense only in --mode=etics\n"
+       if ($version || $output) && $mode ne 'etics';
+
+my $en;
+for (keys %enable_nodes) { $en = 1 if $enable_nodes{$_}; }
+
+my $dis;
+for (keys %disable_nodes) { $dis = 1 if $disable_nodes{$_}; }
+
+die "--enable-* and --disable-* are mutually exclusive\n"
+       if $en && $dis;
+
+die "--module cannot be used with --enable-* or --disable-*\n"
+       if $module && ($en || $dis);
+
+die "$module: unknown module\n" if $module && ! grep $module,@{$lbmodules{lb}},@{$lbmodules{security}},{$lbmodules{jp}};
+
+if ($dis) {
+       for (@nodes) {
+               $enable_nodes{$_} = 1 unless $disable_nodes{$_};
+       }
+}
+
+if (!$en && !$dis) { $enable_nodes{$_} = 1 for (@nodes) } ;
+
+for (keys %enable_nodes) { delete $enable_nodes{$_} unless $enable_nodes{$_}; }
+
+$stagedir = $prefix unless $stagedir;
+
+if ($mode eq 'build') {
+       print "Writing config.status\n";
+       open CONF,">config.status" or die "config.status: $!\n";
+       print CONF "$0 @keeparg\n";
+       close CONF;
+}
+
+
+my @modules;
+my %aux;
+
+if ($module) {
+#      push @modules,split(/[,.]+/,$module);
+       push @modules,$module;
+}
+else {
+       @modules = map(($extranodmod{$_} ? $extranodmod{$_} : 'lb.'.$_),(keys %enable_nodes));
+       
+       my $n;
+
+       do {
+               local $"="\n";
+               $n = $#modules;
+               push @modules,(map @{$deps{$_}},@modules);
+
+               undef %aux; @aux{@modules} = (1) x ($#modules+1);
+               @modules = keys %aux;
+       } while ($#modules > $n);
+}
+
+@aux{@modules} = (1) x ($#modules+1);
+delete $aux{$_} for (split /,/,$staged);
+@modules = keys %aux;
+
+mode_build() if $mode eq 'build';
+mode_checkout() if $mode eq 'checkout';
+mode_etics($module) if $mode eq 'etics';
+
+sub mode_build {
+       print "\nBuilding modules: @modules\n";
+       
+       my @ext = map @{$need_externs{$_}},@modules;
+       my @myjars = map @{$need_jars{$_}},@modules;
+       undef %aux; @aux{@ext} = 1;
+       @ext = keys %aux;
+       undef %aux; @aux{@myjars} = (1) x ($#myjars+1);
+       @myjars = keys %aux;
+       
+       print "\nRequired externals:\n";
+       print "\t$_: $extern_prefix{$_}\n" for @ext;
+       print "\t$_: $jar{$_}\n" for @myjars;
+       print "\nThis is a poor-man configure, it's up to you to have sources and externals there\n\n";
+       
+       mkinc($_) for @modules;
+       
+       print "Creating Makefile\n";
+       
+       open MAK,">Makefile" or die "Makefile: $!\n";
+       
+       print MAK "all: @modules\n\nclean:\n";
+       
+       for (@modules) {
+               my $full = full($_);
+               my $build = $topbuild{$_} ? '': '/build';
+               print MAK "\tcd $full$build && \${MAKE} clean\n"
+       }
+       
+       print MAK "\ndistclean:\n";
+       
+       for (@modules) {
+               my $full = full($_);
+               print MAK $topbuild{$_} ?
+                       "\tcd $full$build && \${MAKE} distclean\n" :
+                       "\trm -rf $full$build\n"
+       }
+       
+       print MAK "\n";
+       
+       for (@modules) {
+               my %ldeps; undef %ldeps;  
+               @ldeps{@{$deps{$_}}} = 1;
+               for my $x (split /,/,$staged) { delete $ldeps{$x}; }
+               my @dnames = $module ? () : keys %ldeps;
+       
+               my $full = full($_);
+               my $build = $topbuild{$_} ? '': '/build';
+       
+               print MAK "$_: @dnames\n\tcd $full$build && \${MAKE} && \${MAKE} install\n\n";
+       }
+       
+       close MAK;
+}
+       
+sub mode_checkout() {
+       for (@modules) {
+               my $module = $_;
+               my $tag = "";
+               if ($lb_tag){
+                       for (@{$lbmodules{lb}}){
+                               if ("lb.".$_ eq $module){
+                                       $tag = '-r '.$lb_tag;
+                               }
+                       }       
+               }
+               if ($lbjp_tag){
+                       for (@{$lbmodules{'lbjp-common'}}){
+                               if ("lbjp-common.".$_ eq $module){
+                                        $tag = '-r '.$lbjp_tag;
+                                }
+                       }
+               }
+               if ($jp_tag){
+                       for (@{$lbmodules{'jp'}}){
+                               if ("jp.".$_ eq $module){
+                                        $tag = '-r '.$jp_tag;
+                               }
+                        }
+               }
+               if ($sec_tag){
+                       for (@{$lbmodules{security}}){
+                               if ("security.".$_ eq $module){
+                                        $tag = '-r '.$sec_tag;
+                                }
+                       }
+               }
+               if ($jobid_tag){
+                       for (@{$lbmodules{jobid}}){
+                               if ("jobid.".$_ eq $module){
+                                        $tag = '-r '.$jobid_tag;
+                                }
+                       }
+               }
+               #if (grep {"lb.".$_ eq $module} @{$lbmodules{lb}}){
+               #       print "found";
+               #}
+               $_ = full($_);
+               print "\n*** Checking out $_\n";
+               system("cvs checkout  $tag $_") == 0 or die "cvs checkout $tag $_: $?\n";
+       }
+}
+
+BEGIN{
+%need_externs_aux = (
+       'lb.client' => [ qw/cppunit:B classads/ ],
+       'lb.client-java' => [ qw/ant:B jdk:B axis:B trustmanager/ ],
+       'lb.common' => [ qw/expat cppunit:B classads/ ],
+       'lb.doc' => [],
+       'lb.logger' => [ qw/cppunit:B log4c/ ],
+       'lb.server' => [ qw/globus_essentials:R globus:B expat cares mysql:R mysql-server:R mysql-devel:B cppunit:B gsoap:B classads voms lcas gridsite log4c/ ],
+       'lb.state-machine' => [ qw/classads/ ],
+       'lb.utils' => [ qw/cppunit:B/ ],
+       'lb.ws-interface' => [],
+       'lb.ws-test' => [ qw/gsoap:B/ ],
+       'lb.types' => [ qw// ],
+       'lb.harvester' => [ qw/postgresql:R/ ],
+       'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B/ ],
+       'lbjp-common.log' => [ qw// ],
+       'lbjp-common.maildir' => [ qw// ],
+       'lbjp-common.server-bones' => [ qw/log4c/ ],
+       'lbjp-common.trio' => [ qw/cppunit:B/ ],
+       'lbjp-common.jp-interface' => [ qw/cppunit:B/ ],
+       'security.gss' =>  [ qw/globus_essentials:R globus:B cares cppunit:B/ ],
+       'security.gsoap-plugin' =>  [ qw/cppunit:B globus_essentials:R globus:B cares:B gsoap:B/ ],
+       'jobid.api-c' =>  [ qw/cppunit:B/ ],
+       'jobid.api-cpp' =>  [ qw/cppunit:B/ ],
+       'jobid.api-java' =>  [ qw/ant:B jdk:B/ ],
+       'jp.client' => [ qw/gsoap libtar globus_essentials:R globus:B/ ],
+        'jp.doc' => [],
+        'jp.index' => [ qw/gsoap globus_essentials:R globus:B/ ],
+        'jp.primary' => [ qw/classads gsoap libtar globus_essentials:R globus:B/ ],
+        'jp.server-common' => [],
+        'jp.ws-interface' => [],
+);
+
+for my $ext (keys %need_externs_aux) {
+       for (@{$need_externs_aux{$ext}}) {
+               /([^:]*)(?::(.*))?/;
+               push @{$need_externs{$ext}},$1;
+               my $type = $2 ? $2 : 'BR';
+               $need_externs_type{$ext}->{$1} = $type;
+       }
+}
+
+%need_jars = (
+       'jobid.api-java' => [ qw/commons-codec/ ],
+       'lb.client-java' => [ qw/commons-lang/ ],
+);
+
+for my $jar (keys %need_jars) {
+       for (@{$need_jars{$jar}}) {
+               $need_externs_type{$jar}->{$_} = 'BR';  # XXX
+       }
+}
+
+%deps_aux = (
+       'lb.client' => [ qw/
+               lb.types:B lb.common
+               lbjp-common.trio
+               jobid.api-cpp jobid.api-c
+               security.gss
+       / ],
+       'lb.client-java' => [ qw/
+               lb.types:B
+               lb.ws-interface:B
+               jobid.api-java
+       / ],
+       'lb.common' => [ qw/
+               jobid.api-cpp jobid.api-c
+               lb.types:B lbjp-common.trio security.gss
+       / ],
+       'lb.doc' => [ qw/lb.types:B/ ],
+       'lb.logger' => [ qw/
+               lbjp-common.trio
+               lbjp-common.log
+               jobid.api-c
+               lb.common
+               security.gss
+       / ],
+       'lb.server' => [ qw/
+               lb.ws-interface lb.types:B lb.common lb.state-machine
+               lbjp-common.db lbjp-common.server-bones lbjp-common.trio lbjp-common.maildir lbjp-common.log
+               jobid.api-c
+               security.gsoap-plugin security.gss
+       / ],
+       'lb.state-machine' => [ qw/lb.types:B lb.common lbjp-common.jp-interface security.gss/ ],
+       'lb.utils' => [ qw/
+               lbjp-common.jp-interface
+               jobid.api-c
+               lbjp-common.trio lbjp-common.maildir
+               lb.client lb.state-machine
+       / ],
+       'lb.ws-test' => [ qw/security.gsoap-plugin lb.ws-interface/ ],
+       'lb.ws-interface' => [ qw/lb.types:B/ ],
+       'lb.types' => [ qw// ],
+       'lb.harvester' => [ qw/
+               jobid.api-c lbjp-common.trio lbjp-common.db lb.common lb.client
+               security.gss
+       / ],
+       'lbjp-common.db' => [ qw/lbjp-common.trio/ ],
+       'lbjp-common.maildir' => [ qw// ],
+       'lbjp-common.server-bones' => [ qw/lbjp-common.log/ ],
+       'lbjp-common.trio' => [ qw// ],
+       'security.gss' =>  [ qw// ],
+       'security.gsoap-plugin' =>  [ qw/security.gss/ ],
+       'jobid.api-c' =>  [ qw// ],
+       'jobid.api-cpp' =>  [ qw/jobid.api-c/ ],
+       'jobid.api-java' =>  [ qw// ],
+
+       'lbjp-common.jp-interface' => [ qw/lbjp-common.db jobid.api-c/ ],
+
+       'jp.client' => [ qw/
+                jp.ws-interface
+                lbjp-common.jp-interface lbjp-common.maildir
+                jobid.api-c
+                security.gsoap-plugin
+        / ],
+       'jp.doc' => [ qw// ],
+       'jp.index' => [ qw/
+                jp.server-common jp.ws-interface
+                lbjp-common.jp-interface lbjp-common.trio lbjp-common.db lbjp-common.server-bones
+                security.gsoap-plugin
+        / ],
+       'jp.primary' => [ qw/
+                jobid.api-c
+                jp.server-common jp.ws-interface
+                lb.state-machine
+                lbjp-common.jp-interface lbjp-common.trio lbjp-common.db lbjp-common.server-bones
+                security.gsoap-plugin
+        / ],
+       'jp.server-common' => [ qw/ 
+                lbjp-common.jp-interface lbjp-common.db
+        / ],
+       'jp.ws-interface' => [ qw// ],
+);
+
+for my $ext (keys %deps_aux) {
+       for (@{$deps_aux{$ext}}) {
+               /([^:]*)(?::(.*))?/;
+               push @{$deps{$ext}},$1;
+               my $type = $2 ? $2 : 'BR';
+               $deps_type{$ext}->{$1} = $type;
+       }
+}
+
+
+%extrafull = ( gridsite=>'org.gridsite.core');
+
+#( java => 'client-java' );
+%extranodmod = (
+       db => 'lbjp-common.db',
+       jpprimary => 'jp.primary',
+       jpindex => 'jp.index',
+       jpclient => 'jp.client',
+);
+
+my @t = qw/lb.client-java jobid.api-java lb.types lbjp-common.log/;
+@topbuild{@t} = (1) x ($#t+1);
+}
+
+sub full
+{
+       my $short = shift;
+       return $extrafull{$short} ? $extrafull{$short} : 'org.glite.'.$short;
+}
+
+sub mkinc
+{
+       my %aux;
+       undef %aux;
+       my @m=qw/
+lb.client lb.doc lb.state-machine lb.ws-interface lb.logger lb.types lb.common lb.server lb.utils lb.ws-test lb.client-java lb.harvester
+security.gss security.gsoap-plugin
+jobid.api-c jobid.api-cpp jobid.api-java
+lbjp-common.db lbjp-common.log lbjp-common.maildir lbjp-common.server-bones lbjp-common.trio lbjp-common.jp-interface
+jp.client jp.doc jp.index jp.primary jp.server-common jp.ws-interface
+/;
+       @aux{@m} = (1) x ($#m+1);
+
+       my $short = shift;
+       my $full = full $short;
+
+       unless ($aux{$short}) {
+               print "Makefile.inc not needed in $full\n";
+               return;
+       }
+
+       my $build = '';
+       
+       unless ($topbuild{$_}) {
+               $build = '/build';
+               unless (-d "$full/build") {
+                       mkdir "$full/build" or die "mkdir $full/build: $!\n";
+               }
+               unlink "$full/build/Makefile";
+               symlink "../Makefile","$full/build/Makefile" or die "symlink ../Makefile $full/build/Makefile: $!\n";
+       }
+
+       open MKINC,">$full$build/Makefile.inc"
+               or die "$full$build/Makefile.inc: $!\n";
+
+       print "Creating $full$build/Makefile.inc\n";
+
+       print MKINC qq{
+PREFIX = $prefix
+stagedir = $stagedir
+thrflavour = $thrflavour
+nothrflavour = $nothrflavour
+libdir = $libdir
+};
+
+       for (@{$need_externs{$short}}) {
+               print MKINC "${_}_prefix = $extern_prefix{$_}\n"
+       }
+
+       for (@{$need_jars{$short}}) {
+               print MKINC "${_}_jar = $jar{$_}\n"
+       }
+
+       my $need_gsoap = 0;
+       for (@{$need_externs{$short}})  { $need_gsoap = 1 if $_ eq 'gsoap'; }
+
+       print MKINC "gsoap_default_version=".gsoap_version()."\n"  if $need_gsoap;
+
+       close MKINC;
+}
+
+my %etics_externs;
+my %etics_projects;
+BEGIN{
+       %etics_externs = (
+               globus_essentials=>'vdt_globus_essentials',
+               globus=>'globus',
+               cares=>'c-ares',
+               voms=>'org.glite.security.voms-api-cpp',
+               gridsite=>'org.gridsite.shared',
+               lcas=>'org.glite.security.lcas',
+               trustmanager=>'org.glite.security.trustmanager',
+       );
+       %etics_projects = (
+               vdt=>[qw/globus globus_essentials/],
+               'org.glite'=>[qw/voms gridsite lcas/],
+       );
+};
+
+sub mode_etics {
+       $fmod = shift;
+
+       die "$0: --module required with --etics\n" unless $fmod;
+       
+       my ($subsys,$module) = split /\./,$fmod;
+
+       my ($major,$minor,$rev,$age);
+
+       if ($version) {
+               $version =~ /([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)-(.+)/;
+               ($major,$minor,$rev,$age) = ($1,$2,$3,$4);
+       }
+       else { 
+               open V,"org.glite.$subsys.$module/project/version.properties"
+                       or die "org.glite.$subsys.$module/project/version.properties: $!\n";
+       
+               while ($_ = <V>) {
+                       chomp;
+                       ($major,$minor,$rev) = ($1,$2,$3) if /module\.version\s*=\s*([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)/;
+                       $age = $1 if /module\.age\s*=\s*([[:digit:]]+)/;
+               }
+               close V;
+       }
+
+       my @copts = ();
+       my %ge;
+       @ge{@{$etics_projects{'org.glite'}}} = (1) x ($#{$etics_projects{'org.glite'}}+1);
+
+       for (@{$need_externs{"$subsys.$module"}}) {
+           if ($need_externs_type{"$subsys.$module"}->{$_}=~/B/) {
+               my $eext = $etics_externs{$_} ? $etics_externs{$_} : $_;
+               push @copts,$ge{$_} ? "--with-$_=\${stageDir}" : "--with-$_=\${$eext.location}";
+           }
+       }
+
+       for (@{$need_jars{"$subsys.$module"}}) {
+               my $eext = $etics_externs{$_} ? $etics_externs{$_} : $_;
+
+               push @copts,"--with-$_ \${$eext.location}/$_*.jar";
+       }
+
+
+       my $conf = "glite-$subsys-${module}_R_${major}_${minor}_${rev}_${age}";
+       my $file = $output ? $output : "$conf.ini";
+       open C,">$file" or die "$file: $!\n";
+
+       my $buildroot = $topbuild{"$subsys.$module"} ? '' : "build.root = build";
+
+       my $confdir = $topbuild{"$subsys.$module"} ? '..' : '../..';
+
+       my $package_description = "";
+       my $package_summary = "";
+
+       if (-e "org.glite.$subsys.$module/project/package.description") {
+               open V, "org.glite.$subsys.$module/project/package.description";
+               $package_description = join ("", <V>);
+               close V;
+               chomp $package_description;
+               $package_description =~ s/\n/\\n/g; 
+               $package_description = "package.description = $package_description";
+       } 
+       else { 
+               print STDERR "package.description not found for $subsys.$module!\n"; }
+
+       if (-e "org.glite.$subsys.$module/project/package.summary") {
+               open V, "org.glite.$subsys.$module/project/package.summary";
+               $package_summary = join ("", <V>);
+               close V;
+               chomp $package_summary;
+               $package_summary =~ s/\n/\\n/g; 
+               $package_summary = "package.summary = $package_summary";
+       } 
+       else { 
+               print STDERR "package.summary not found for $subsys.$module!\n"; }
+
+
+       print STDERR "Writing $file\n";
+       print C qq{
+[Configuration-$conf]
+profile = None
+moduleName = org.glite.$subsys.$module
+displayName = $conf
+description = org.glite.$subsys.$module
+projectName = org.glite
+age = $age
+deploymentType = None
+tag = $conf
+version = $major.$minor.$rev
+path = \${projectName}/\${moduleName}/\${version}/\${platformName}/\${packageName}-\${version}-\${age}.tar.gz
+
+[Platform-default:VcsCommand]
+displayName = None
+description = None
+tag = cvs -d \${vcsroot} tag -R \${tag} \${moduleName}
+branch = None
+commit = None
+checkout = cvs -d \${vcsroot} co -r \${tag} \${moduleName}
+
+[Platform-default:BuildCommand]
+postpublish = None
+packaging = None
+displayName = None
+description = None
+doc = None
+prepublish = None
+publish = None
+compile = make
+init = None
+install = make install
+clean = make clean
+test = make check
+configure = cd $confdir && \${moduleName}/configure --thrflavour=\${globus.thr.flavor} --nothrflavour=\${globus.nothr.flavor} --prefix=\${prefix} --stage=\${stageDir} --libdir=\${libdir} --module $subsys.$module @copts
+checkstyle = None
+
+[Platform-default:Property]
+$buildroot
+$package_description
+$package_summary
+
+[Platform-default:DynamicDependency]
+
+};
+       for (@{$need_externs{"$subsys.$module"}},@{$need_jars{"$subsys.$module"}}) {
+               my $eext = $etics_externs{$_} ? $etics_externs{$_} : $_;
+
+               my $proj = 'externals';
+               for my $p (keys %etics_projects) {
+                       for $m (@{$etics_projects{$p}}) {
+                               $proj = $p if $m eq $_;
+                       }
+               }
+
+               my $type = $need_externs_type{"$subsys.$module"}->{$_};
+               print C "$proj|$eext = $type\n";
+       }
+
+       for (@{$deps{"$subsys.$module"}}) {
+               my $type = $deps_type{"$subsys.$module"}->{$_};
+               print C "org.glite|org.glite.$_ = $type\n";
+       }
+
+       close C;
+}
+
+sub gsoap_version {
+       local $_;
+       my $gsoap_version;
+       open S,"$extern_prefix{gsoap}/bin/soapcpp2 -v 2>&1 |" or die "$extern_prefix{gsoap}/bin/soapcpp2: $!\n";
+
+       while ($_ = <S>) {
+               chomp;
+
+               $gsoap_version = $1 if /The gSOAP Stub and Skeleton Compiler for C and C\+\+ ([.[:digit:][:alpha:]]+)$/;
+       }
+       close S;
+       return $gsoap_version;
+}
+
+sub getlibdir {
+       if ( -e "/etc/debian_version") { # We are on Debian
+               $lib64="lib";
+               $lib32="lib32"; }
+       else { # Another distribution
+               $lib64="lib64";
+               $lib32="lib";   }
+        $libdir=$lib32;
+
+        open INP, "uname -s | "; # Check kernel name
+        $kname= <INP>;
+        chomp($kname);
+        close INP;
+
+        if ( $kname == "Linux") {
+                $arch = ("x86_64\npowerpc\nppc64\n");
+
+                open INP, "uname -p | "; # Check processor type
+                $procname= <INP>;
+                chomp($procname);
+                close INP;
+
+                if ($arch =~/^$procname\n/) {
+                        return ($lib64); }
+
+                open INP, "uname -m | "; # Check machine hardware
+                $machname= <INP>;
+                chomp($machname);
+                close INP;
+
+                if ($arch =~/^$machname\n/) {
+                        return ($lib64); }
+
+                # special cases (hyperlink lib64, Debian)
+                if (-l "/usr/lib64") {
+                        $libdir=$lib32; }
+
+                # if /usr/lib64 doesn't exist at all (AIX)
+                unless ( -e "/usr/lib64" ) {
+                        $libdir=$lib32; }
+        }
+
+        if ( $kname == "SunOS") {
+                if (-e "/usr/lib/64") {
+                $libdir="lib/64"; }
+        }
+
+        return $libdir;
+}
+
+sub usage {
+       my @ext = keys %extern_prefix;
+       my @myjars, keys %jar;
+
+       print STDERR qq{
+usage: $0 options
+
+General options (defaults in []):
+  --prefix=PREFIX              destination directory [./stage]
+  --staged=module,module,...   what is already in PREFIX (specify without org.glite.)
+  --thrflavour=flavour
+  --nothrflavour=flavour       threaded and non-treaded flavours [gcc64dbgpthr,gcc64dbg]
+  --listmodules=subsys          list modules of a subsystem
+  --libdir=libdir              typically [lib,lib64] postfix
+  
+Mode of operation:
+  --mode={checkout|build|etics}        what to do [build]
+  
+What to build:
+  --module=module              build this module only (mostly in-Etics operation)
+  --enable-NODE                        build this "node" (set of modules) only. Available nodes are
+                                       @{$lbmodules{lb}},@{$lbmodules{security}}
+  --disable-NODE               don't build this node
+  --lb-tag=tag                 checkout LB modules with specific tag
+  --jp-tag=tag                 checkout JP modules with specific tag
+  --lbjp-common-tag=tag         checkout lbjp-common modules with specific tag
+  --security-tag=tag           checkout security modules with specific tag
+  --jobid-tag=tag              checkout jobid modules with specific tag
+
+Dependencies:
+  --with-EXTERNAL=PATH         where to look for an external. Required externals
+                               (not all for all modules) are:
+                                       @ext
+  --with-JAR=JAR               where to look for jars. Required jars are:
+                                       @myjars
+                               Summary of what will be used is always printed
+
+};
+
+}
diff --git a/org.glite.lb.harvester/doc/INSTALL b/org.glite.lb.harvester/doc/INSTALL
new file mode 100644 (file)
index 0000000..f5ff9c9
--- /dev/null
@@ -0,0 +1,42 @@
+Requirements
+============
+
+1) gLite
+- client L&B libraries:
+  - glite-jobid-api-c
+  - glite-lb-common
+  - glite-lb-client
+  - glite-security-gss
+  - globus essential libraries (threaded flavour),
+    use the vesion with the external SSL, not with bundled SSL (!)
+ - glite-lbjp-common-db (build only)
+ - mysql-devel (build only)
+2) postgresql-devel
+
+
+Steps
+=====
+
+./configure
+make
+make install
+
+Use './configure --help' for the options.
+
+
+Manual way
+==========
+
+configure is simple script generating Makefile.inc. You can build harvester
+straight away by make defining the variables manually. For example with gLite
+installed in ~/glite/stage:
+
+(rm Makefile.inc)
+make stagedir=$HOME/glite/stage
+
+
+Testing
+=======
+
+Test for basic functionality covered by 'test.sh' script in sources.
+See './test.sh --help'.
diff --git a/org.glite.lb.harvester/doc/README b/org.glite.lb.harvester/doc/README
new file mode 100644 (file)
index 0000000..f1c393d
--- /dev/null
@@ -0,0 +1,81 @@
+Introduction
+============
+
+L&B Harvester gathers information about jobs from L&B servers using effective
+L&B notification mechanism. It manages notifications and keeps them in
+a persistent storage (file or database table) to reuse later on next launch.
+It takes care about refreshing notifications and queries L&B servers back when
+some notification on expires.
+
+The tool was initially written for Real Time Monitor (project at Imperial
+College in London), later was extended with messaging mechanism for WLCG.
+
+
+Requirements
+============
+
+- lastUpdateTime index on L&B servers
+- harvester identity in super users file on L&B servers
+
+
+Launch (with msg-publish sending messages)
+=========================================
+
+Harvester is sending notifications via msg-publish infrastructure. List of the
+L&B server to harvest is specified via -c option.
+
+1) with newer LB 2.0 servers:
+
+  glite-lb-harvester -c servers.txt -C certfile -K keyfile --wlcg
+
+2) with older LB servers (backward compatible but greedy notifications):
+
+  glite-lb-harvester -c servers.txt -C certfile -K keyfile --wlcg --old
+
+Custom configuration of messaging:
+       --wlcg-binary $HOME/bin/msg-publish
+       --wlcg-topic org.wlcg.usage.JobStatus2
+       --wlcg-config $HOME/etc/msg-publish.conf.wlcg
+
+
+Launch (Real Time Monitor and storing to the database)
+======================================================
+
+Harvester is using postgres database. Table 'lb20' with L&B servers to
+harvest (read-only), table 'jobs' with result job states (read/write). It's
+possible to specify L&B servers list by file instead of 'lb20' table,
+via -c option.
+
+  glite-lb-harvester -C certfile -K keyfile --pg rtm/@:rtm
+
+The connection string after '--pg' is in format:
+  USER/PASSWORD@HOST:DATABASE
+Database schema in 'test.sql'.
+
+
+Other recommended options
+=========================
+
+Use 'glite-lb-harvester --help' for additional options.
+
+For example:
+ - deamonizing and using syslog:
+       '--daemonize --pidfile /var/run/glite-lb-harvester.pid'
+ - decreasing verbosity:
+       '-d 2' (2 for errors and warnings only)
+
+
+Stop
+====
+
+In non-daemon mode CTRL-C can be used, in daemon mode using specified
+pidfile:
+
+ kill `cat /var/run/glite-lb-harvester.pid`
+
+pidfile will vanish after exit.
+
+All notifications are preserved on LB servers, and will expire later. You can
+purge them now, if they won't be needed:
+
+ glite-lb-harvester --cleanup
diff --git a/org.glite.lb.harvester/doc/glite-lb-harvester.sgml b/org.glite.lb.harvester/doc/glite-lb-harvester.sgml
new file mode 100644 (file)
index 0000000..5d9f75a
--- /dev/null
@@ -0,0 +1,480 @@
+<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry id='glitelbharvester'>
+
+       <refmeta>
+               <refentrytitle>glite-lb-harvester</refentrytitle>
+               <manvolnum>1</manvolnum>
+               <refmiscinfo>EU EGEE Project</refmiscinfo>
+       </refmeta>
+
+       <refnamediv id='name'>
+               <refname>glite-lb-harvester</refname>
+               <refpurpose>daemon for processing L&amp;B notifications</refpurpose>
+       </refnamediv>
+
+       <refsynopsisdiv id='synopsis'>
+               <cmdsynopsis>
+                       <command>glite-lb-harvester</command>
+
+                       <arg><group choice='plain'>
+                               <arg>-h</arg>
+                               <arg>--help</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-v</arg>
+                               <arg>--version</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-d</arg>
+                               <arg>--debug</arg>
+                       </group> <replaceable>LEVEL</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-D</arg>
+                               <arg>--daemon</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-i</arg>
+                               <arg>--pidfile</arg>
+                       </group> <replaceable>PIDFILE</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-s</arg>
+                               <arg>--threads</arg>
+                       </group> <replaceable>N</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-t</arg>
+                               <arg>--ttl</arg>
+                       </group> <replaceable>TIME</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-H</arg>
+                               <arg>--history</arg>
+                       </group> <replaceable>TIME</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-c</arg>
+                               <arg>--config</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-m</arg>
+                               <arg>--pg</arg>
+                       </group> <replaceable>USER/PWD@SERVER:DBNAME</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-n</arg>
+                               <arg>--notifs</arg>
+                       </group> <replaceable>FILE</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-p</arg>
+                               <arg>--port</arg>
+                       </group> <replaceable>PORT</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-C</arg>
+                               <arg>--cert</arg>
+                       </group> <replaceable>FILE</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-K</arg>
+                               <arg>--key</arg>
+                       </group> <replaceable>FILE</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-o</arg>
+                               <arg>--old</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-l</arg>
+                               <arg>--cleanup</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-u</arg>
+                               <arg>--no-purge</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>-w</arg>
+                               <arg>--wlcg</arg>
+                       </group></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>--wlcg-binary</arg>
+                       </group> <replaceable>EXECUTABLE</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>--wlcg-topic</arg>
+                       </group> <replaceable>TOPIC</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>--wlcg-config</arg>
+                       </group> <replaceable>FILENAME</replaceable></arg>
+
+                       <arg><group choice='plain'>
+                               <arg>--wlcg-flush</arg>
+                       </group></arg>
+               </cmdsynopsis>
+       </refsynopsisdiv>
+
+       <refsect1>
+               <title>DESCRIPTION</title>
+               <para>
+L&amp;B Harvester gathers information about jobs from L&amp;B servers using efficient
+L&amp;B notification mechanism. It manages notifications and keeps them in
+a persistent storage (file or database table) to reuse later on next launch.
+It takes care about refreshing notifications and queries L&amp;B servers back when
+some notification expires.
+
+The tool was initially written for Real Time Monitor (project at Imperial
+College in London), later was extended by MSG publish messaging mechanism for WLCG.
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>Requirements</title>
+               <para>
+It is required on L&amp;B servers side:
+                       <itemizedlist>
+                               <listitem><para>
+<filename>lastUpdateTime</filename> index, see "Changing Index Configuration" section in L&amp;B Admin Guide
+                               </para></listitem>
+                               <listitem><para>
+L&amp;B harvester identity (certification subject) in super users file
+                               </para></listitem>
+                       </itemizedlist>
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>OPTIONS</title>
+       
+               <variablelist>
+                       <varlistentry>
+                               <term><option>-h</option>|<option>--help</option></term>
+                               <listitem><para>
+Print short usage.
+                               </para></listitem>
+                       </varlistentry>
+               </variablelist>
+
+               <variablelist>
+                       <varlistentry>
+                       <term><option>-v</option>|<option>--version</option></term>
+                       <listitem><para>
+Print harvester version identifier.
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-d</option>|<option>--debug</option></term>
+                       <listitem><para>
+                       Verbosity level:
+                               <variablelist>
+                                       <varlistentry><term>0</term><listitem><para>error only</para></listitem></varlistentry>
+                                       <varlistentry><term>1</term><listitem><para>warnings</para></listitem></varlistentry>
+                                       <varlistentry><term>2</term><listitem><para>info/progress</para></listitem></varlistentry>
+                                       <varlistentry><term>3</term><listitem><para>debug</para></listitem></varlistentry>
+                                       <varlistentry><term>4</term><listitem><para>insane</para></listitem></varlistentry>
+                                       <varlistentry><term>+8 (8,9,10,11,12)</term><listitem><para>don't fork and no preventive restarts</para></listitem></varlistentry>
+                               </variablelist>
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-D</option>|<option>--daemonize</option></term>
+                       <listitem><para>
+Daemonize and detach from console. Error messages are directed to syslog.
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-i</option>|<option>--pidfile</option></term>
+                       <listitem><para>
+The file with process ID. Automatically removed on shutdown.
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-s</option>|<option>--threads</option></term>
+                       <listitem><para>
+Number of threads (slaves). Configured L&amp;B servers are equally distributed between threads.
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-t</option>|<option>--ttl</option></term>
+                       <listitem><para>
+Validity (time to live) of the notifications. Daemon regularly refreshes notification in advance as needed.
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-H</option>|<option>--history</option></term>
+                       <listitem><para>
+Historic dive limit in seconds. &lt;= means unlimited.
+                       </para><para>
+                       When staring, the L&amp;B harvester queries the L&amp;B servers for existing jobs. It queries L&amp;B server when notification expires too and can't be refreshed on time. This parameter is used for limit, how deep into history L&amp;B harvester should go.
+                       </para><para>
+                       Another usage of this parameter is for derivation of the maximal time of retries. When some L&amp;B server is inaccessible or it is in error condition, harvester linearly increases retry time. The maximal retry time is half of this parameter.
+                       </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                       <term><option>-c</option>|<option>--config</option></term>
+                       <listitem><para>
+                       Config file name with list of L&amp;B servers. When used together with database option <option>-m</option> (<option>--pg</option>), this parameter has precedence before <filename>lb20</filename> table.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-m</option>|<option>--pg</option></term>
+                               <listitem><para>
+Database connection string in the <filename>USER/PWD@SERVER:DBNAME</filename> form. There are used following tables in database:
+                                       <itemizedlist>
+                                               <listitem><para>
+       <filename>lb20</filename> - the list of L&amp;B servers is taken from this table. But when is specified option <option>-c</option> (<option>--config</option>) too, the file has precedence before this table.
+                                               </para><para>
+There is kept a column <filename>monitored</filename> in too: if there is any inactive notification because of errors on given L&amp;B server (one expired or it was unable to create a new one), the <filename>false</filename> value is set. After refreshing or creating the notification, the value is set back to <filename>true</filename>.
+                                               </para></listitem>
+                                               <listitem><para>
+       <filename>jobs</filename> - table for storing job states. Each record is updated for each incoming notification - when state of the job changes in L&amp;B server.
+                                               </para></listitem>
+                                       </itemizedlist></para><para>
+Database schema can be found in source code of <filename>org.glite.lb.harvester</filename>: <filename>examples/test.sql</filename>
+                                       </para><para>
+Developer note: information about notifications are kept in a file. It is possible to compile a binary keeping states in the database. It is used in the test in <filename>examples</filename> source directory.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-n</option>|<option>--notifs</option></term>
+                               <listitem><para>
+File for internal usage in L&amp;B harvester. There is kept persistent information about active notifications or errors on L&amp;B servers. Default is <filename>/var/tmp/notifs.txt</filename>.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-p</option>|<option>--port</option></term>
+                               <listitem><para>
+Specifies the port for listening and requests L&amp;B nodes to send notification messages only to this port. May be needed for networks behind NAT or behind firewalls.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-C</option>|<option>--cert</option></term>
+                               <listitem><para>
+X509 certificate file.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-K</option>|<option>--key</option></term>
+                               <listitem><para>
+X509 key file.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-o</option>|<option>--old</option></term>
+                               <listitem><para>
+"silly" mode for L&amp;B servers &lt; 2.0. In this mode transfer of the notification is not optimized at all. On the other hand it will work with older L&amp;B servers.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-l</option>|<option>--cleanup</option></term>
+                               <listitem><para>
+Cleans up all active notifications and quits.
+                               </para><para>
+Each  notification automatically expires. But if you know, than notifications used in previous run of L&amp;B harvester won;t be needed, it is recommended to clean up the notifications and spare the resources on L&amp;B servers (queue with undelivered notification messages and matching rules).
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-u</option>|<option>--no-purge</option></term>
+                               <listitem><para>
+By default jobs are purged from local database when purged on L&amp;B server. This option forces keeping all jobs in database, only with changed state to 'Purged'.
+                               </para><para>
+For using together with <option>-m</option> (<option>--pg</option>).
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>-w</option>|<option>--wlcg</option></term>
+                               <listitem><para>
+Enables delivery to MSG publish. Messages are sent by executing a binary with proper parameters.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>--wlcg-binary</option></term>
+                               <listitem><para>
+Full path to msg-publish binary executable, which is called for sending messages. Default is <filename>/usr/bin/msg-publish</filename>.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>--wlcg-topic</option></term>
+                               <listitem><para>
+Topic used in MSG publish messages. Default is <filename>org.wlcg.usage.jobStatus</filename>.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>--wlcg-config</option></term>
+                               <listitem><para>
+Config file used in MSG publish. Default is <filename>/etc/msg-publish/msg-publish.conf</filename>.
+                               </para></listitem>
+                       </varlistentry>
+
+                       <varlistentry>
+                               <term><option>--wlcg-flush</option></term>
+                               <listitem><para>
+Messages are sent to MSG publish in batches by default. This option enforce sending the messages one by one on each notification from L&amp;B server - for each job state change.
+                               </para></listitem>
+                       </varlistentry>
+
+               </variablelist>
+       </refsect1>
+
+       <refsect1>
+               <title>ENVIRONMENT</title>
+
+               <variablelist>
+                       <varlistentry>
+                               <term>GLITE_LB_HARVESTER_NO_REMOVE</term>
+                               <listitem><para>
+<filename>0</filename> or <filename>false</filename> instructs L&amp;B harvester to not remove temporary files with sent messages for MSG publish. By default temporary files with successfully sent messages are removed. Files with failed messages are always preserved.
+                               </para><para>
+Intended for debugging purposes.
+                               </para></listitem>
+                       </varlistentry>
+               </variablelist>
+       </refsect1>
+
+       <refsect1>
+               <title>EXAMPLES</title>
+
+               <refsect2>
+                       <title>MSG publish infrastructure</title>
+                       <para>
+Harvester will send notifications using msg-publish infrastructure. List of the L&amp;B servers to harvest is specified in config file specified by <option>-c</option> option:
+                       </para>
+                       <variablelist>
+                               <varlistentry>
+                                       <term><command>glite-lb-harvester -c servers.txt -C certfile -K keyfile --wlcg</command></term>
+                                       <listitem><para>
+With newer L&amp;B servers &gt;= 2.0.
+                                       </para></listitem>
+                               </varlistentry>
+                               <varlistentry>
+                                       <term><command>glite-lb-harvester -c servers.txt -C certfile -K keyfile --wlcg --old</command></term>
+                                       <listitem><para>
+With older L&amp;B servers &lt; 2.0 (backward compatible but greedy notifications).
+                                       </para></listitem>
+                               </varlistentry>
+                       </variablelist>
+
+                       <para>
+Custom configuration examples for MSG publish:
+                               <itemizedlist>
+                                       <listitem><para>
+<option>--wlcg-binary</option> <filename>$HOME/bin/msg-publish</filename>
+                                       </para></listitem><listitem><para>
+<option>--wlcg-topic</option> <filename>org.wlcg.usage.JobStatus2</filename>
+                                       </para></listitem><listitem><para>
+<option>--wlcg-config</option> <filename>$HOME/etc/msg-publish.conf.wlcg</filename>
+                                       </para></listitem>
+                               </itemizedlist>
+                       </para>
+               </refsect2>
+
+               <refsect2>
+                       <title>Real Time Monitor</title>
+                       <para>
+Harvester will use postgres database. Table <filename>lb20</filename> with L&amp;B servers to harvest (read-only), table <filename>jobs</filename> for result job states (read/write):
+                       </para>
+                       <variablelist>
+                               <varlistentry>
+                                       <term><command>glite-lb-harvester -C certfile -K keyfile --pg rtm/@:rtm -p 9004</command></term>
+                                       <listitem><para>
+In this case the L&amp;B harvester will connect to database <filename>rtm</filename> on <filename>localhost</filename> as user <filename>rtm</filename>. For incoming notification it will request and listen only on port 9004.
+                                       </para></listitem>
+                               </varlistentry>
+                       </variablelist>
+               </refsect2>
+
+               <refsect2>
+                       <title>Other recommended options</title>
+                       <para>
+Use <command>glite-lb-harvester --help</command> for the whole summary.
+                       </para><para>
+For example:
+                               <variablelist>
+                                       <varlistentry>
+                                               <term><option>--daemonize --pidfile /var/run/glite-lb-harvester.pid</option></term>
+                                               <listitem><para>
+Daemonizing and using syslog.
+                                               </para></listitem>
+                                       </varlistentry>
+
+                                       <varlistentry>
+                                               <term><option>-d 2</option></term>
+                                               <listitem><para>
+Decreasing verbosity (2 for errors and warnings only).
+                                               </para></listitem>
+                                       </varlistentry>
+                               </variablelist>
+                       </para>
+               </refsect2>
+       </refsect1>
+
+       <refsect1>
+               <title>EXIT</title>
+               <para>
+In non-daemon mode CTRL-C can be used.
+               </para><para>
+Use the pidfile in daemon mode (pidfile will vanish after exit):
+               </para><para>
+<command>kill `cat /var/run/glite-lb-harvester.pid`</command>
+               </para><para>
+All notifications are preserved on LB servers, and will expire later. You can
+purge them at once, if they won't be needed:
+               </para><para>
+<command>glite-lb-harvester --cleanup</command>
+               </para>
+       </refsect1>
+
+       <refsect1>
+               <title>EXIT STATUS</title>
+               <variablelist>
+                       <varlistentry>
+                               <term>0</term>
+                               <listitem><para>Success.</para></listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term>1</term>
+                               <listitem><para>Reloading, used only internally for preventive restarts.</para></listitem>
+                       </varlistentry>
+                       <varlistentry>
+                               <term>2</term>
+                               <listitem><para>Error occurred. Messages go on console (foreground run) or into syslog (daemon run), depending on verbosity.</para></listitem>
+                       </varlistentry>
+               </variablelist>
+       </refsect1>
+
+       <refsect1>
+               <title>AUTHOR</title>
+               <para>gLite L&amp;B product team, CESNET.</para>
+       </refsect1>
+
+</refentry>
diff --git a/org.glite.lb.harvester/examples/test.sh b/org.glite.lb.harvester/examples/test.sh
new file mode 100755 (executable)
index 0000000..4bfba32
--- /dev/null
@@ -0,0 +1,876 @@
+#! /bin/sh
+
+
+usage() {
+cat <<EOF
+
+ ./test.sh
+
+ Testing of the L&B harvester using local daemons and databases.
+
+ Requirements:
+  - working L&B software (ideally with passed org.glite.testsuites.ctb/LB/test)
+  - mysql and postgresql databases, with sufficient access
+    pg_hba.conf content for example:
+      local all all trust all
+  - created user \$RTM_NAME (rtm by default) in postgres DB
+  - proxy certificate
+  - writable working directory with harvester built with -DRTM_SQL_STORAGE_ENABLED=1
+    (or via 'make test')
+
+ non-default configuration is possible via following env variables or
+ using ~/.glite.conf:
+   GLITE_LOCATION..................path to glite SW
+   GLOBUS_LOCATION.................path to globus SW
+   GLITE_HOST_CERT.................path to host certificate
+   GLITE_HOST_KEY..................path to host key
+   GLITE_LB_TEST_DB................L&B connection string 
+                                   (default lbserver/@localhost:lbserver20test,
+                                    autocreating the database when empty)
+   GLITE_RTM_TEST_DB...............L&B connection string, existing user with
+                                   privileges required
+                                   (default rtm/@localhost:rtmtest,
+                                    autocreating the database when empty)
+   GLITE_MYSQL_ROOT_USER...........mysql root user (default: root)
+   GLITE_MYSQL_ROOT_PASSWORD.......mysql root password (default: none)
+   GLITE_PG_ROOT_USER..............postgres root user (default: postgres)
+   GLITE_LB_TEST_SERVER_PORT.......(default 10000)
+   GLITE_LB_TEST_PIDFILE...........(default /tmp/glite-lb-test.pid)
+   GLITE_RTM_TEST_PIDFILE..........(default /tmp/glite-rtm-test.pid)
+   GLITE_RTM_TEST_TTL..............notif validity (default 60 seconds)
+   GLITE_RTM_TEST_ADDITIONAL_ARGS..additional arguments for harvester
+
+EOF
+}
+
+
+init() {
+       # get the configuration
+       GLITE_LOCATION=${GLITE_LOCATION:-"/opt/glite"}
+       [ -f /etc/glite.conf ] && . /etc/glite.conf
+       [ -f $HOME/.glite.conf ] && . $HOME/.glite.conf
+       
+       GLOBUS_LOCATION=${GLOBUS_LOCATION:-"/opt/globus"}
+       
+        if [ -n "$GLITE_HOST_CERT" -a -n "$GLITE_HOST_KEY" ] ;then
+               X509_USER_CERT="$GLITE_HOST_CERT"
+               X509_USER_KEY="$GLITE_HOST_KEY"
+       fi
+       
+#      if [ -z "$X509_USER_CERT" -o -z "$X509_USER_KEY" ] ; then
+               if [ -e "$GLOBUS_LOCATION/bin/grid-proxy-info" ] ; then
+                       timeleft=`$GLOBUS_LOCATION/bin/grid-proxy-info 2>&1| \
+                               grep timeleft| sed 's/^.* //'`
+                       if [ "$timeleft" = "0:00:00" -o -z "$timeleft" ]; then 
+                               echo "Proxy certificate check failed."\
+                               " Aborting."
+                               exit 1
+                       fi
+               else
+                       echo "Can't check proxy cert (grid-proxy-info not found). If you do not have valid proxy certificate, set GLITE_HOST_KEY/GLITE_HOST_KEY - otherwise tests will fail!"
+               fi
+#      fi
+       identity=`X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} $GLOBUS_LOCATION/bin/grid-proxy-info 2>&1| \
+               grep identity| sed 's/^[^/]*//'`
+
+       if [ -z "$GLITE_LB_TEST_DB" ]; then
+               GLITE_LB_TEST_DB="lbserver/@localhost:lbserver20test"
+                       need_new_lb_db=1;
+       fi
+       DB_USER=`echo $GLITE_LB_TEST_DB| sed 's!/.*$!!'`
+       DB_HOST=`echo $GLITE_LB_TEST_DB| sed 's!^.*@!!' | sed 's!:.*!!'`
+       DB_NAME=`echo $GLITE_LB_TEST_DB| sed 's!^.*:!!'`
+       MYSQL_ARGS="-u ${GLITE_MYSQL_ROOT_USER:-root}"
+       [ -z "$GLITE_MYSQL_ROOT_PASSWORD" ] || ARGS="--password=${GLITE_MYSQL_ROOT_PASSWORD} $MYSQL_ARGS"
+
+       if [ -z "$GLITE_RTM_TEST_DB" ]; then
+               GLITE_RTM_TEST_DB="rtm/@localhost:rtmtest"
+                       need_new_rtm_db=1;
+       fi
+       RTM_USER=`echo $GLITE_RTM_TEST_DB| sed 's!/.*$!!'`
+       RTM_HOST=`echo $GLITE_RTM_TEST_DB| sed 's!^.*@!!' | sed 's!:.*!!'`
+       RTM_NAME=`echo $GLITE_RTM_TEST_DB| sed 's!^.*:!!'`
+       PG_ARGS="-U ${GLITE_PG_ROOT_USER:-postgres}"
+
+       #other stuff
+       GLITE_LB_TEST_SERVER_PORT=${GLITE_LB_TEST_SERVER_PORT:-"10000"}
+       GLITE_LB_TEST_PIDFILE=${GLITE_LB_TEST_PIDFILE:-"/tmp/glite-lb-test.pid"}
+       GLITE_RTM_TEST_PIDFILE=${GLITE_RTM_TEST_PIDFILE:-"/tmp/glite-rtm-test.pid"}
+       GLITE_RTM_TEST_TTL=${GLITE_RTM_TEST_TTL:-"60"}
+
+       jobreg="$GLITE_LOCATION/examples/glite-lb-job_reg -m `hostname -f`:${GLITE_LB_TEST_SERVER_PORT} -s UserInterface"
+       logev="$GLITE_LOCATION/bin/glite-lb-logevent -x -S `pwd`/LB/proxy.sockstore.sock -U localhost"
+       purge="$GLITE_LOCATION/bin/glite-lb-purge"
+       [ -x "$purge" ] || purge="$GLITE_LOCATION/sbin/glite-lb-purge"
+       for dir in "$GLITE_LOCATION/examlpes" "`pwd`/../build" "`pwd`"; do
+               if [ -x "$dir/glite-lb-harvester-dbg" ]; then
+                       rtm="$dir/glite-lb-harvester-dbg"
+               fi
+               if [ -x "$dir/harvester-dbg" ]; then
+                       rtm="$dir/harvester-dbg"
+               fi
+       done
+       if [ -z "$rtm" ]; then
+               echo "glite-lb-harvester-dbg not found"
+               return 1
+       fi
+
+       if echo "$GLITE_RTM_TEST_ADDITIONAL_ARGS" | grep -- '[^-]\?\(--old\>\|-o\>\)' >/dev/null; then
+               n_notifs=1
+       else
+               n_notifs=2
+       fi
+
+       rm -f log
+}
+
+
+drop_db() {
+return 0
+       [ -z "$lb_db_created" ] || mysqladmin -f $MYSQL_ARGS drop "$DB_NAME"
+       [ -z "$rtm_db_created" ] || dropdb $PG_ARGS "$RTM_NAME"
+}
+
+
+create_db() {
+       echo -n "mysql."
+       # create database when needed
+       if [ "x$need_new_lb_db" = "x1" ]; then
+               mysqladmin -f $MYSQL_ARGS drop $DB_NAME > /dev/null 2>&1
+               echo -n "."
+               mysqladmin -f $MYSQL_ARGS create $DB_NAME && \
+               echo -n "."
+               mysql $MYSQL_ARGS -e "GRANT ALL on $DB_NAME.* to $DB_USER@$DB_HOST" && \
+               echo -n "."
+               mysql -u $DB_USER $DB_NAME -h $DB_HOST < $GLITE_LOCATION/etc/glite-lb-dbsetup.sql || return $?
+               echo -n "."
+               mkdir -p `pwd`/LB
+               cat > `pwd`/LB/glite-lb-index.conf << EOF
+[
+       JobIndices = {
+               [ type = "system"; name = "lastUpdateTime" ]
+       }
+]
+EOF
+               LBDB="$GLITE_LB_TEST_DB" $GLITE_LOCATION/bin/glite-lb-bkindex -r `pwd`/LB/glite-lb-index.conf || return $?
+               lb_db_created="1"
+               echo -n "."
+       else
+               cleanup_mysql || return $?
+       fi
+       echo -n "OK psql."
+       if [ "x$need_new_rtm_db" = "x1" ]; then
+               dropdb $PG_ARGS "$RTM_NAME" >/dev/null 2>&1
+               echo -n "."
+#              createuser $PG_ARGS -A -D "$RTM_NAME" >/dev/null 2>&1
+#              echo -n "."
+               createdb $PG_ARGS --encoding "UTF-8" --owner "$RTM_USER" "$RTM_NAME" >psql-create.log 2>&1 || return $?
+               rm psql-create.log
+               echo -n "."
+               rtm_db_created="1"
+               echo "\i test.sql" | psql -AtF ',' -U "$RTM_USER" "$RTM_NAME" >/dev/null || return $?
+               echo -n "."
+       else
+               cleanup_pg || return $?
+       fi
+       echo "OK"
+}
+
+
+cleanup_mysql() {
+       cat << EOF | mysql -u $DB_USER $DB_NAME -h $DB_HOST || return $?
+DELETE FROM acls;
+DELETE FROM events;
+DELETE FROM events_flesh;
+DELETE FROM jobs;
+DELETE FROM long_fields;
+DELETE FROM notif_jobs;
+DELETE FROM notif_registrations;
+DELETE FROM server_state;
+DELETE FROM short_fields;
+DELETE FROM states;
+DELETE FROM status_tags;
+DELETE FROM users;
+DELETE FROM zombie_jobs;
+DELETE FROM zombie_prefixes;
+DELETE FROM zombie_suffixes;
+EOF
+       echo -n "."
+}
+
+
+cleanup_pg() {
+               cat << EOF | psql -AtF ',' -U "$RTM_USER" "$RTM_NAME" >/dev/null || return $?
+DELETE FROM jobs;
+DELETE FROM notifs;
+EOF
+       echo -n "."
+}
+
+
+run_daemons() {
+       mkdir -p LB/dump LB/purge LB/voms 2>/dev/null
+
+       # checks
+       if [ -f "${GLITE_LB_TEST_PIDFILE}" ]; then
+               echo "L&B server already running (${GLITE_LB_TEST_PIDFILE}, `cat ${GLITE_LB_TEST_PIDFILE}`)"
+               quit=1
+       fi
+       if [ -f "${GLITE_RTM_TEST_PIDFILE}" ]; then
+               echo "L&B harvester already running (${GLITE_RTM_TEST_PIDFILE}, `cat ${GLITE_RTM_TEST_PIDFILE}`)"
+               quit=1
+       fi
+       if [ -e "`pwd`/LB/notif.sock" ]; then
+               if [ "`lsof -t $(pwd)/LB/notif.sock | wc -l`" != "0" ]; then
+                       echo "Notification interlogger already running (using LB/notif.sock, `lsof -t $(pwd)/LB/notif.sock`)"
+                       quit=1
+               fi
+       fi
+       if [ -e "`pwd`/LB/proxy-il.sock" ]; then
+               if [ "`lsof -t $(pwd)/LB/proxy-il.sock | wc -l`" != "0" ]; then
+                       echo "Proxy interlogger already running (using LB/proxy-il.sock, `lsof -t $(pwd)/LB/proxy-il.sock`)"
+                       quit=1
+               fi
+       fi
+       [ -z "$quit" ] || exit 1
+
+       # run L&B server
+       echo -n "L"
+       X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} \
+       $GLITE_LOCATION/bin/glite-lb-bkserverd \
+         -m $GLITE_LB_TEST_DB \
+         -p $GLITE_LB_TEST_SERVER_PORT -w $(($GLITE_LB_TEST_SERVER_PORT + 3))\
+         -i ${GLITE_LB_TEST_PIDFILE} \
+         --withproxy -o `pwd`/LB/proxy.sock\
+         --proxy-il-sock `pwd`/LB/proxy-il.sock --proxy-il-fprefix `pwd`/LB/proxy-data \
+         -D `pwd`/LB/dump -S `pwd`/LB/purge \
+         -V `pwd`/LB/voms \
+         --notif-il-sock `pwd`/LB/notif.sock --notif-il-fprefix `pwd`/LB/notif-data \
+         --super-user "$identity" > `pwd`/LB/glite-lb-test-pre.log 2>&1
+       if [ x"$?" != x"0" ]; then
+               cat `pwd`/LB/glite-lb-test-pre.log
+               echo FAILED
+               drop_db;
+               exit 1
+       fi
+       echo -n "B "
+
+       # run L&B interlogger
+       echo -n "L"
+       X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} \
+       $GLITE_LOCATION/bin/glite-lb-interlogd \
+         --file-prefix `pwd`/LB/proxy-data --socket `pwd`/LB/proxy-il.sock > `pwd`/LB/glite-interlog-test-pre.log 2>&1
+       if [ x"$?" != x"0" ]; then
+               cat `pwd`/LB/glite-interlog-test-pre.log
+               echo FAILED
+               kill_bkserver
+               drop_db;
+               exit 1
+       fi
+       echo -n "I "
+
+       # run L&B notification interlogger
+       echo -n "N"
+       X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} \
+       $GLITE_LOCATION/bin/glite-lb-notif-interlogd \
+         --file-prefix `pwd`/LB/notif-data --socket `pwd`/LB/notif.sock > `pwd`/LB/glite-notif-test-pre.log 2>&1
+       if [ x"$?" != x"0" ]; then
+               cat `pwd`/LB/glite-notif-test-pre.log
+               echo FAILED
+               kill_daemons
+               drop_db;
+               exit 1
+       fi
+       echo -n "I "
+
+       if ! start_harvester; then
+               kill_daemons;
+               drop_db;
+               exit 1
+       fi
+
+       # wait for pidfiles
+       i=0
+       while [ ! -s "${GLITE_LB_TEST_PIDFILE}" -a $i -lt 20 ]; do
+               sleep 0.1
+               i=$(($i+1))
+       done
+       if [ ! -s "${GLITE_LB_TEST_PIDFILE}" ]; then
+               echo "Can't startup L&B server."
+               kill_daemons;
+               drop_db;
+               exit 1
+       fi
+
+       echo -n "notifs."
+       pg_wait 20 "SELECT refresh FROM notifs WHERE notifid IS NOT NULL" $n_notifs || return $?
+       refresh=`echo "$result" | head -n 1`
+       if [ -z "$refresh" ]; then
+               echo "FAIL"
+               return 1
+       fi
+}
+
+
+start_harvester() {
+       # run L&B harvester server
+       echo -n "R"
+       rm -Rf RTM
+       mkdir RTM 2>/dev/null
+       echo "`hostname -f`:${GLITE_LB_TEST_SERVER_PORT}" > `pwd`/RTM/config.txt
+       X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} \
+       ${rtm} \
+         -m $GLITE_RTM_TEST_DB \
+         --pidfile ${GLITE_RTM_TEST_PIDFILE} \
+         --ttl ${GLITE_RTM_TEST_TTL} \
+         --history $((GLITE_RTM_TEST_TTL / 2)) \
+         --debug 12 \
+         --config `pwd`/RTM/config.txt \
+         --daemonize ${GLITE_RTM_TEST_ADDITIONAL_ARGS} 2>`pwd`/RTM/glite-rtm-test-pre.log >`pwd`/RTM/notifs.log
+       if [ x"$?" != x"0" ]; then
+               cat `pwd`/RTM/glite-rtm-test-pre.log
+               echo FAILED
+               return 1
+       fi
+
+       i=0
+       while [ ! -s "${GLITE_RTM_TEST_PIDFILE}" -a $i -lt 20 ]; do
+               sleep 0.1
+               i=$(($i+1))
+       done
+       if [ ! -s "${GLITE_RTM_TEST_PIDFILE}" ]; then
+               echo "Can't startup L&B harvester."
+               kill_daemons;
+               drop_db;
+               exit 1
+       fi
+
+       echo -n "M "
+}
+
+
+cleanup_harvester() {
+       echo -n "cleaning up..."
+       X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} \
+       ${rtm} \
+         -m $GLITE_RTM_TEST_DB \
+         --cleanup \
+         --debug 12 ${GLITE_RTM_TEST_ADDITIONAL_ARGS} >`pwd`/RTM/glite-rtm-test-cleanup.log 2>&1
+       if [ x"$?" != x"0" ]; then
+               cat `pwd`/RTM/glite-rtm-test-cleanup.log
+               echo FAILED
+               return 1
+       fi
+       echo -n "OK "
+}
+
+
+kill_daemons() {
+       pid1=`cat ${GLITE_LB_TEST_PIDFILE} 2>/dev/null`
+       [ -f "${GLITE_RTM_TEST_PIDFILE}" ] && pid2=`cat ${GLITE_RTM_TEST_PIDFILE}`
+       pid3=`lsof -t $(pwd)/LB/notif.sock 2>/dev/null`
+       pid4=`lsof -t $(pwd)/LB/proxy-il.sock 2>/dev/null`
+       [ ! -z "$pid1" ] && kill $pid1
+       [ ! -z "$pid2" ] && kill -2 $pid2
+       [ ! -z "$pid3" ] && kill $pid3
+       [ ! -z "$pid4" ] && kill $pid4
+       sleep 1;
+       [ ! -z "$pid1" ] && kill -9 $pid1 2>/dev/null
+       [ ! -z "$pid2" ] && kill -9 $pid2 2>/dev/null
+       [ ! -z "$pid3" ] && kill -9 $pid3 2>/dev/null
+       [ ! -z "$pid4" ] && kill -9 $pid4 2>/dev/null
+       rm -f "${GLITE_LB_TEST_PIDFILE}" "${GLITE_RTM_TEST_PIDFILE}"
+       rm -f `pwd`/LB/*.sock
+}
+
+
+kill_bkserver() {
+       pid=`cat ${GLITE_LB_TEST_PIDFILE} 2>/dev/null`
+       if [ ! -z "$pid1" ]; then
+               kill $pid;
+               sleep 1;
+               kill -9 $pid
+       fi
+       rm -f "${GLITE_LB_TEST_PIDFILE}"
+}
+
+
+kill_harvester() {
+       pid=`cat ${GLITE_RTM_TEST_PIDFILE} 2>/dev/null`
+       if [ ! -z "$pid1" ]; then
+               kill $pid
+               sleep 1;
+               kill -9 $pid 2>/dev/null
+       fi
+       rm -f "${GLITE_RTM_TEST_PIDFILE}"
+}
+
+
+reg() {
+       echo -n "R"
+       echo $jobreg $@ >> log
+       $jobreg $@ > jobreg.tmp
+       if [ $? -ne 0 ]; then
+               cat jobreg.tmp
+               rm -f jobreg.tmp
+               echo " FAIL!"
+               return 1;
+       fi
+       script=`cat jobreg.tmp | tail -n 2`
+       rm -f jobreg.tmp
+       EDG_JOBID=
+       EDG_WL_SEQUENCE=
+       eval $script
+       if [ -z "$EDG_JOBID" -o -z "$EDG_WL_SEQUENCE" ]; then
+               echo " FAIL!"
+               return 1;
+       fi
+       echo -n "G "
+}
+
+
+ev() {
+       echo -n "E"
+       echo $logev -j "$EDG_JOBID" -c "$EDG_WL_SEQUENCE" "$@" >> log
+       $logev -j "$EDG_JOBID" -c "$EDG_WL_SEQUENCE" "$@" 2> logev-err.tmp >logev.tmp
+       if [ $? -ne 0 ]; then
+               echo " FAIL!"
+               return 2;
+       fi
+       EDG_WL_SEQUENCE=`cat logev.tmp`
+       rm logev.tmp logev-err.tmp
+       echo -n "V "
+}
+
+
+pg_get() {
+       result=
+       lines=
+       echo "$1" | psql -AtF ',' -U "$RTM_USER" "$RTM_NAME" > psql.tmp
+       if [ $? != 0 ]; then
+               return $?
+       fi
+       result="`cat psql.tmp`"
+       lines=`wc -l psql.tmp | sed 's/^[ ]*//' | cut -f1 -d' '`
+#      rm psql.tmp
+       return 0
+}
+
+
+pg_wait() {
+       timeout=$(($1*2))
+       sql="$2"
+       n="$3"
+
+       i=0
+       found=0
+       result=
+       echo -n "S"
+       echo "`date '+%Y-%m-%d %H:%M:%S'` $sql" >> log
+       while [ "$found" = "0" -a $i -lt $timeout ]; do
+               pg_get "$sql" || return $?
+               echo -n "."
+               if [ -z "$n" ]; then
+                       if [ "$lines" != "0" ]; then found=1; fi
+               else
+                       if [ "$lines" = "$n" ]; then found=1; fi
+               fi
+               if [ "$found" = "0" ]; then sleep 0.5; fi
+               i=$(($i+1))
+       done
+       echo -n "Q "
+       result="$result"
+       echo "`date '+%Y-%m-%d %H:%M:%S'` $lines lines" >> log
+       if [ ! -z "$result" ]; then
+               echo "$result" | sed -e 's/\(.*\)/\t\1/' >> log
+       fi
+       return 0
+}
+
+
+my_get() {
+       result=
+       lines=
+       echo "`date '+%Y-%m-%d %H:%M:%S'` $1" >> log
+       echo "$1" | mysql -B -u "$DB_USER" "$DB_NAME" > mysql.tmp
+       if [ $? != 0 ]; then
+               return $?
+       fi
+       result=`cat mysql.tmp | tail -n +2`
+       lines=`echo "$result" | grep -v '^$' | wc -l | sed 's/^[ ]*//'`
+       echo "`date '+%Y-%m-%d %H:%M:%S'` $lines lines" >> log
+       if [ ! -z "$result" ]; then
+               echo "$result" | sed -e 's/\(.*\)/\t\1/' >> log
+       fi
+#      rm -f mysql.tmp
+       return 0
+}
+
+
+# notif propagation
+test_basic() {
+       ok=0
+
+       # submited
+       echo -n "submitted..."
+       reg || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Submitted'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       # waiting
+       echo -n "waiting..."
+       ev -s NetworkServer -e Accepted --from='UserInterface' --from_host=`hostname -f` --from_instance="pid$$" || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Waiting'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       # running
+       echo -n "running..."
+       ev -s LogMonitor -e Running --node="worker node" || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Running'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       ok=1
+       echo "OK"
+}
+
+
+# proper notif registration cleanup
+test_rebind() {
+       ok=0
+
+       # ---- active ---
+       echo -n "$n_notifs notifications "
+       my_get "SELECT notifid FROM notif_registrations" || return $?
+       # STATUS and JDL
+       if [ "$lines" != "$n_notifs" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       # ---- store & stop ---
+       echo -n "store&quit"
+       pid=`cat ${GLITE_RTM_TEST_PIDFILE}`
+       kill $pid
+       i=0
+       while [ -s "${GLITE_RTM_TEST_PIDFILE}" -a $i -lt 200 ]; do
+               echo -n "."
+               sleep 0.5
+               i=$(($i+1))
+       done
+       if [ -s "${GLITE_RTM_TEST_PIDFILE}" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK notifs "
+       my_get "SELECT notifid FROM notif_registrations" || return $?
+       if [ "$lines" != "$n_notifs" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       # ---- launch & rebind ---
+       if ! start_harvester; then
+               kill_daemons;
+               drop_db;
+               exit 1
+       fi
+
+       echo -n "bind"
+       pg_wait 20 "SELECT notifid FROM notifs WHERE notifid IS NOT NULL" $n_notifs || return $?
+       if [ x"$lines" != x"$n_notifs" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       echo -n "Done "
+       ev -s LogMonitor -e Done --status_code=OK --reason="Finished, yeah!" --exit_code=0 || return $?
+       pg_wait 20 "SELECT jobid, state FROM jobs WHERE state='Done'"
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       ok=1
+       echo "OK"
+}
+
+
+test_cleanup() {
+       ok=0
+
+       # ---- deep stop ---
+       echo -n "deep quit"
+       pid=`cat ${GLITE_RTM_TEST_PIDFILE}`
+       kill -2 $pid
+       i=0
+       while [ -s "${GLITE_RTM_TEST_PIDFILE}" -a $i -lt 200 ]; do
+               echo -n "."
+               sleep 0.5
+               i=$(($i+1))
+       done
+       if [ -s "${GLITE_RTMTESTPIDFILE}" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       echo -n "$n_notifs notifications..."
+       my_get "SELECT notifid FROM notif_registrations" || return 1
+       if [ "$lines" != "$n_notifs" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       cleanup_harvester || return $?
+       echo -n "0 notifications..."
+       my_get "SELECT notifid FROM notif_registrations" || return 1
+       if [ "$lines" != "0" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       echo -n "cleandb."
+       cleanup_pg || return $?
+       start_harvester || return $?
+
+       echo -n "notifs."
+       pg_wait 20 "SELECT refresh FROM notifs WHERE notifid IS NOT NULL" $n_notifs || return $?
+       refresh=`echo "$result" | head -n 1`
+       if [ -z "$refresh" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       ok=1
+       echo "OK"
+}
+
+
+test_refresh() {
+       ok=0
+
+       echo -n "refresh."
+       pg_wait $((GLITE_RTM_TEST_TTL * 3 / 4)) "SELECT notifid FROM notifs WHERE notifid IS NOT NULL AND refresh>'$refresh'" $n_notifs || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       ok=1
+       echo "OK"
+}
+
+
+test_jdl() {
+       ok=0
+
+#      kill_daemons
+#      cleanup_mysql && cleanup_pg || return $?
+#      run_daemons || return $?
+
+       # need to wait for notifications to avoid bootstrap
+       echo -n "notifs."
+       pg_wait 20 "SELECT refresh FROM notifs WHERE notifid IS NOT NULL" $n_notifs || return $?
+       refresh=`echo "$result" | head -n 1`
+       if [ -z "$refresh" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       echo -n "submitted..."
+       reg || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Submitted'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       echo -n "waiting..."
+       cat > jdl.txt << EOF
+[
+ VirtualOrganisation = "TestingVO";
+]
+EOF
+       ev -s NetworkServer -e Accepted --from='UserInterface' --from_host=`hostname -f` --from_instance="pid$$" || return $?
+       ev -s NetworkServer -e EnQueued --queue "very long and chaotic queue" --job=`pwd`/jdl.txt --result START || return $?
+       ev -s NetworkServer -e EnQueued --queue "very long and chaotic queue" --job="`cat jdl.txt`" --result OK || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Waiting'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       echo -n "waiting and VO..."
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Waiting' AND vo='TestingVO'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       #
+       # test JDL via VO change
+       #
+       # never do it at home ;-)
+       #
+
+       echo -n "changed JDL..."
+       ev -s NetworkServer -e EnQueued --queue "very long and chaotic queue" --job="[ VirtualOrganisation=\"TestingVO2\";]" --result OK || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Waiting' AND vo='TestingVO2'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "OK "
+
+       echo -n "changed after waiting..."
+       ev -s WorkloadManager -e EnQueued --queue "very long and chaotic queue" --destination LogMonitor --dest_host localhost --dest_instance pid$$ --job "(car 'testing=true)"  --result=OK || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Ready' AND vo='TestingVO2'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+       echo -n "ready..."
+       ev -s NetworkServer -e EnQueued --queue "very long and chaotic queue" --job="[ VirtualOrganisation=\"TestingVO3\";]" --result OK || return $?
+       pg_wait 10 "SELECT jobid, state FROM jobs WHERE state='Waiting' AND vo='TestingVO3'" || return $?
+       if [ -z "$result" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       ok=1
+       echo "OK"
+}
+
+
+test_purge() {
+       ok=0
+
+       echo -n "purge."
+       pg_get "SELECT jobid FROM jobs" || return $?
+       if [ -z "$lines" -o $lines -le 0  ]; then
+               echo "no jobs! FAIL"
+               return 0
+       fi
+       echo -n "P"
+       jobunique=`echo "$result" | head -n 1 | tr -d '\n'`
+       jobid="https://`hostname -f`:${GLITE_LB_TEST_SERVER_PORT}/$jobunique"
+       echo $jobid > jobs
+       echo "${purge} -a1 -c1 -n1 -e1 -o1 -m "`hostname -f`:${GLITE_LB_TEST_SERVER_PORT}" -j jobs" >> log
+       echo "  jobs = `cat jobs` | tr -d '\n'" >> log
+       X509_USER_KEY=${X509_USER_KEY} X509_USER_CERT=${X509_USER_CERT} ${purge} -l -a1 -c1 -n1 -e1 -o1 -m "`hostname -f`:${GLITE_LB_TEST_SERVER_PORT}" -j jobs 2> purge-err.tmp >purge.tmp
+       if [ $? -ne 0 ]; then
+               echo " FAIL!"
+               return 2;
+       fi
+       rm -f jobs
+       echo -n "R "
+
+       pg_wait 10 "SELECT * FROM jobs WHERE jobid='$jobunique'" 0 || return $?
+       if [ x"$lines" != x"0" ]; then
+               echo "FAIL"
+               return 0
+       fi
+
+       ok=1
+       echo "OK"
+}
+
+
+quit() {
+       if [ x"$started" = x"" ]; then
+               kill_daemons
+               drop_db
+       fi
+       exit 1
+}
+
+
+fatal() {
+       echo "Fatal error, end"
+       quit
+}
+
+
+start() {
+       echo -n "Launch: "
+       create_db || fatal
+       run_daemons || fatal
+       echo "OK"
+       started=1
+}
+
+
+stop() {
+       kill_daemons
+       drop_db
+}
+
+
+test() {
+       echo -n "Basic: "
+       test_basic || fatal
+       if [ $ok != 1 ]; then quit; fi
+
+       echo -n "Rebind: "
+       test_rebind || fatal
+       if [ $ok != 1 ]; then quit; fi
+
+       echo -n "Cleanup: "
+       test_cleanup || fatal
+       if [ $ok != 1 ]; then quit; fi
+
+       echo -n "Refresh: "
+       test_refresh || fatal
+       if [ $ok != 1 ]; then quit; fi
+
+       echo -n "JDL: "
+       test_jdl || fatal
+       if [ $ok != 1 ]; then quit; fi
+
+#      echo -n "Purge: "
+#      test_purge || fatal
+#      if [ $ok != 1]; then quit; fi
+}
+
+
+case x"$1" in
+xstart)
+       init
+       start
+       ;;
+
+xstop)
+       init
+       stop
+       ;;
+
+xtest)
+       init
+       test
+       ;;
+
+x)
+       init
+       start
+       test
+       stop
+       ;;
+
+*)
+       usage
+       exit 1
+esac
diff --git a/org.glite.lb.harvester/examples/test.sql b/org.glite.lb.harvester/examples/test.sql
new file mode 100644 (file)
index 0000000..15736f6
--- /dev/null
@@ -0,0 +1,55 @@
+--
+-- Inicialization (replace pgsql by actual postgres superuser):
+--
+-- 1) grant privileges, someting like this in $data/pg_hba.conf:
+--     local all all trust
+--
+-- 2) create user:
+--     createuser -U pgsql rtm
+--
+-- 3) crate database:
+--     createuser -U pgsql rtm
+--
+-- 4) create tables:
+--     psql -U rtm rtm < test.sql
+--
+
+CREATE TABLE "jobs" (
+       jobid VARCHAR PRIMARY KEY,
+       lb VARCHAR,
+       ce VARCHAR,
+       queue VARCHAR,
+       rb VARCHAR,
+       ui VARCHAR,
+       state VARCHAR,
+       state_entered TIMESTAMP,
+       rtm_timestamp TIMESTAMP,
+       active BOOLEAN,
+       state_changed BOOLEAN,
+       registered TIMESTAMP,
+       vo VARCHAR
+);
+
+CREATE TABLE "lb20" (
+       ip TEXT NOT NULL,
+       branch TEXT NOT NULL,
+       serv_version TEXT NOT NULL,
+       monitored BOOLEAN DEFAULT FALSE,
+       last_seen DATE,
+       first_seen DATE,
+
+       PRIMARY KEY(ip)
+);
+
+CREATE TABLE "notifs" (
+       lb VARCHAR,
+       port INTEGER,
+       notifid VARCHAR,
+       notiftype VARCHAR,
+       valid TIMESTAMP,
+       refresh TIMESTAMP,
+       last_update TIMESTAMP,
+       errors INTEGER,
+
+       PRIMARY KEY(lb, port, notiftype)
+);
diff --git a/org.glite.lb.harvester/project/ChangeLog b/org.glite.lb.harvester/project/ChangeLog
new file mode 100644 (file)
index 0000000..3198875
--- /dev/null
@@ -0,0 +1,20 @@
+1.0.0-1
+- Initial version
+
+1.0.1-1
+- Changes for Real Time Monitor
+- Workaround for older Globus
+- Minor memleak fixes
+
+1.0.2-1
+- Less verbosity in reporting single notifications
+- Fixed postgres dependency
+
+1.0.3-1
+- Extended documentation
+- Fixed purging
+- Fixed build issues
+
+1.0.4-1
+- Fixed 'nodb' build issues
+
diff --git a/org.glite.lb.harvester/project/package.description b/org.glite.lb.harvester/project/package.description
new file mode 100644 (file)
index 0000000..c969e8c
--- /dev/null
@@ -0,0 +1,3 @@
+L&B Harvester gathers information about jobs from L&B servers using efficient L&B notification mechanism. It manages notifications and keeps them in a persistent storage (file or database table) to reuse later on next launch. It takes care about refreshing notifications and queries L&B servers back when some notification expires.
+
+The tool was initially written for Real Time Monitor (project at Imperial College in London), later was extended by MSG publish messaging mechanism for WLCG.
diff --git a/org.glite.lb.harvester/project/package.summary b/org.glite.lb.harvester/project/package.summary
new file mode 100644 (file)
index 0000000..062972a
--- /dev/null
@@ -0,0 +1 @@
+Enhanced L&B notification client.
diff --git a/org.glite.lb.harvester/project/version.properties b/org.glite.lb.harvester/project/version.properties
new file mode 100644 (file)
index 0000000..8366e02
--- /dev/null
@@ -0,0 +1,2 @@
+module.version=1.0.4
+module.age=1
diff --git a/org.glite.lb.harvester/src/harvester.c b/org.glite.lb.harvester/src/harvester.c
new file mode 100644 (file)
index 0000000..c105189
--- /dev/null
@@ -0,0 +1,2691 @@
+#ident "$Header$"
+
+/*
+ * Real time monitor.
+ */
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <getopt.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <time.h>
+#include <glite/security/glite_gss.h>
+#ifdef WITH_LBU_DB
+#include <glite/lbu/trio.h>
+#include <glite/lbu/db.h>
+#endif
+#include <glite/lb/context.h>
+#ifndef WITH_OLD_LB
+#include <glite/lb/connpool.h>
+#endif
+#include <glite/lb/notification.h>
+#include <glite/lb/consumer.h>
+
+
+// default number of the threads/sockets
+#define RTM_THREADS 5
+// requested notification life in seconds
+#define RTM_NOTIF_TTL 86400
+// consider end of the notification life sooner
+#define RTM_NOTIF_TTL_TO_DEAD 2
+// poll timeout in seconds
+#define RTM_NOTIF_READ_TIMEOUT 5
+// recheck LB server after error in seconds
+#define RTM_ERROR_REPEAT_RATE 120
+// initial read loop time (can be infinity)
+#define RTM_NOTIF_LOOP_MAX_TIME 1800
+// idle "quit" poll
+#define RTM_IDLE_POLL_TIME 0.5
+// purge & summary jobs poll time
+#define RTM_SUMMARY_POLL_TIME 600
+// preventive suicide against memleaks and ugly things (12 h)
+#define RTM_SUICIDE_TIME 43200
+
+#define RTM_SUMMARY_JOBS 100
+
+#define RTM_DB_TABLE_JOBS "jobs"
+#define RTM_DB_TABLE_LBS "lb20"
+#define DBPAR(N) ("$" (N))
+#define DBAMP "\""
+
+// debug message level: insane, debug, progress, warning, error
+#define INS 4
+#define DBG 3
+#define INF 2
+#define WRN 1
+#define ERR 0
+#define DEBUG_LEVEL_MASK 7
+#define DEBUG_GUARD_MASK 8
+
+// internal quit codes
+#define RTM_QUIT_RUN 0
+#define RTM_QUIT_CLEANUP 1
+#define RTM_QUIT_PRESERVE 2
+#define RTM_QUIT_RELOAD 3
+
+// exit codes
+#define RTM_EXIT_OK 0
+#define RTM_EXIT_RELOAD 1
+#define RTM_EXIT_ERROR 2
+
+#define RTM_NOTIF_TYPE_STATUS 1
+#define RTM_NOTIF_TYPE_JDL 2
+#define RTM_NOTIF_TYPE_OLD 3
+#define RTM_NOTIF_TYPE_DONE 4
+
+#ifdef RTM_NO_COLORS
+#define RTM_TTY_RED ""
+#define RTM_TTY_GREEN ""
+#define RTM_TTY_RST ""
+#else
+#define RTM_TTY_RED "\e[1;31m"
+#define RTM_TTY_GREEN "\e[1;32m"
+#define RTM_TTY_RST "\e[0;39m"
+#endif
+
+#ifndef LINE_MAX
+#define LINE_MAX 1023
+#endif
+
+#define RTM_FILE_NOTIFS "/var/tmp/notifs.txt"
+#define RTM_FILE_NOTIF_PRINTF "%s\t%s\t%s\t%s\t%s\t%d\n"
+#define RTM_FILE_NOTIF_SCANF "%511[^\t]\t%511[^\t]\t%511[^\t]\t%511[^\t]\t%511[^\t]\t%511[^\t\r\n]\n"
+#define RTM_FILE_NOTIF_NUM 6
+
+#define WLCG_FILENAME_TEMPLATE "/tmp/wlcg_%02d_XXXXXX"
+#define WLCG_COMMAND_MESSAGE "/opt/lcg/bin/msg-publish -c /opt/lcg/etc/msg-publish.conf org.wlcg.usage.jobStatus %s"
+#define WLCG_BINARY "/usr/bin/msg-publish"
+#define WLCG_CONFIG "/etc/msg-publish/msg-publish.conf"
+#define WLCG_TOPIC "org.wlcg.usage.jobStatus"
+
+
+#ifdef WITH_OLD_LB
+#define glite_jobid_t edg_wlc_JobId
+#define glite_jobid_create edg_wlc_JobIdCreate
+#define glite_jobid_recreate edg_wlc_JobIdRecreate
+#define glite_jobid_dup edg_wlc_JobIdDup
+#define glite_jobid_free edg_wlc_JobIdFree
+#define glite_jobid_parse edg_wlc_JobIdParse
+#define glite_jobid_unparse edg_wlc_JobIdUnparse
+#define glite_jobid_getServer edg_wlc_JobIdGetServer
+#define glite_jobid_getServerParts edg_wlc_JobIdGetServerParts
+#define glite_jobid_getUnique edg_wlc_JobIdGetUnique
+#define edg_wll_NotifNew(CTX, CONDS, FLAGS, SOCK, LADDR, ID, VALID) edg_wll_NotifNew((CTX), (CONDS), (SOCK), (LADDR), (ID), (VALID))
+#define edg_wll_JDLField(STAT, NAME) NULL
+#ifndef GLITE_JOBID_DEFAULT_PORT
+#define GLITE_JOBID_DEFAULT_PORT GLITE_WMSC_JOBID_DEFAULT_PORT
+#endif
+#endif
+
+// TODO: ipv6? :-)
+
+typedef struct {
+       edg_wll_NotifId id;  // notification context (after bootstrap/rebind)
+       char *id_str;        // notification id string
+       int type;            // for distinguish various notifications on one LB
+       char *server;        // LB server hostname
+       unsigned int port;   // LB server port
+       time_t valid;        // maximal validity of the notification
+       time_t refresh;      // when try to refresh (before expiration),
+                            // used for retry time after error too
+       double last_update;  // last change from the server
+       int active;          // helper (compare LB servers and notifications,
+                            // if to save to the persistent storage)
+       int error;           // errors counter
+} notif_t;
+
+typedef struct {
+       int id;
+       pthread_t thread;
+       notif_t *notifs;
+       int nservers;
+       time_t next_refresh;
+       char time_s[100];
+       char *dash_filename;
+       int dash_fd;
+#ifdef WITH_LBU_DB
+       glite_lbu_DBContext dbctx;
+       glite_lbu_Statement insertcmd, updatecmd, updatecmd_vo, updatecmd_mon, deletecmd;
+       int dbcaps;
+#endif
+} thread_t;
+
+typedef struct {
+       char *local_address;
+       int nthreads;
+       char *config_file;
+       char *notif_file;
+       int debug;
+       int guard;
+       int daemonize;
+       char *pidfile;
+       int dive;
+       char *dbcs;  // DB connection string
+       char *cert, *key;
+       int ttl;     // requested time to live (validity) of the notifications
+       int cleanup;        // if to clean up notifications on LB servers
+       int wlcg;           // dashboard messaging
+       int wlcg_no_remove; // don't remove temporary files (for debugging)
+       char *wlcg_binary;  // path msg-publish binary
+       char *wlcg_config;  // msg config file
+       char *wlcg_topic;   // msg topic
+       int wlcg_flush;     // send message for eachnotification
+       int silly;          // old LB 1.9 mode
+       int no_purge;       // disabled reaction on purge state
+
+       int nservers;
+       notif_t *notifs;
+} config_t;
+
+typedef struct {
+       notif_t *notifs;
+       int n, maxn;
+       pthread_mutex_t lock;
+       double last_check;
+       int was_summary; // flag for debugging
+#ifdef WITH_LBU_DB
+       glite_lbu_DBContext dbctx;
+#endif
+} db_t;
+
+
+static const char rcsid[] = "@(#)$Id$";
+
+static int rtm2syslog[] = {
+       LOG_ERR,
+       LOG_WARNING,
+       LOG_INFO,
+       LOG_DEBUG,
+       LOG_DEBUG,
+};
+
+static const struct option opts[] = {
+       { "wlcg-binary", required_argument,     NULL,   0},
+       { "wlcg-config", required_argument,     NULL,   0},
+       { "wlcg-topic", required_argument,      NULL,   0},
+       { "wlcg-flush", no_argument,            NULL,   0},
+       { "help",       no_argument,            NULL,   'h'},
+       { "version",    no_argument,            NULL,   'v'},
+       { "threads",    required_argument,      NULL,   's'},
+       { "debug",      required_argument,      NULL,   'd'},
+       { "daemonize",  no_argument,            NULL,   'D'},
+       { "pidfile",    required_argument,      NULL,   'i'},
+       { "ttl",        required_argument,      NULL,   't'},
+       { "history",    required_argument,      NULL,   'H'},
+       { "config",     required_argument,      NULL,   'c'},
+       { "notifs",     required_argument,      NULL,   'n'},
+       { "port",       required_argument,      NULL,   'p'},
+       { "pg",         required_argument,      NULL,   'm'},
+       { "cert",       required_argument,      NULL,   'C'},
+       { "key",        required_argument,      NULL,   'K'},
+       { "wlcg",       no_argument,            NULL,   'w'},
+       { "old",        no_argument,            NULL,   'o'},
+       { "cleanup",    no_argument,            NULL,   'l'},
+       { "no-purge",   no_argument,            NULL,   'u'},
+       { NULL,         no_argument,            NULL,   0}
+};
+
+static const char *opts_line = "hvs:d:Di:t:H:c:n:p:m:C:K:wolu";
+
+config_t config = {
+       local_address: NULL,
+       nthreads: RTM_THREADS,
+       config_file: NULL,
+       notif_file: NULL,
+       debug: DBG,
+       guard: 1,
+       dive: 10800,
+       dbcs: NULL,
+       cert: NULL,
+       key: NULL,
+       ttl: RTM_NOTIF_TTL,
+       cleanup: 0,
+       wlcg: 0,
+       silly: 0,
+       no_purge: 0,
+
+       nservers: 0,
+       notifs: NULL,
+};
+db_t db = {
+       notifs: NULL,
+       n: 0,
+       maxn: 0,
+       lock: PTHREAD_MUTEX_INITIALIZER,
+#ifdef WITH_LBU_DB
+       dbctx: NULL
+#endif
+};
+thread_t *threads = NULL;
+volatile sig_atomic_t quit = RTM_QUIT_RUN;
+
+static int listen_port = 0;
+
+#define lprintf(T, LEVEL, FMT, ARGS...) \
+       if ((LEVEL) <= config.debug) lprintf_func((T), (LEVEL), (FMT), ##ARGS)
+#define lprintf_ctx(T, LEVEL, CTX, FMT, ARGS...) \
+       if ((LEVEL) <= config.debug) lprintf_ctx_func((T), (CTX), (LEVEL), (FMT), ##ARGS)
+#define lprintf_dbctx(T, LEVEL, FMT, ARGS...) \
+       if ((LEVEL) <= config.debug) lprintf_dbctx_func((T), (LEVEL), (FMT), ##ARGS)
+
+#ifdef WITH_OLD_LB
+int edg_wll_gss_initialize() {
+       if (globus_module_activate(GLOBUS_GSI_GSSAPI_MODULE) != GLOBUS_SUCCESS) return EINVAL;
+       return 0;
+}
+#endif
+
+void lvprintf_func(thread_t *t, const char *description, int level, const char *fmt, va_list ap) {
+       char prefix[10];
+       char *msg, *line;
+       
+       if (t) snprintf(prefix, sizeof prefix, "[%02d]", t->id);
+       else memcpy(prefix, "[main]", 8);
+       vasprintf(&msg, fmt, ap);
+       if (description) asprintf(&line, "%s %s, %s\n", prefix, msg, description);
+       else asprintf(&line, "%s %s\n", prefix, msg);
+       free(msg);
+
+       if (level <= WRN && !config.daemonize) fprintf(stderr, RTM_TTY_RED);
+       if (config.daemonize) {
+               openlog(NULL, LOG_PID | LOG_CONS, LOG_DAEMON);
+               syslog(rtm2syslog[level], "%s", line);
+               closelog();
+       } else {
+               fputs(line, stderr);
+       }
+       if (level <= WRN && !config.daemonize) fprintf(stderr, RTM_TTY_RST);
+
+       free(line);
+}
+
+
+void lprintf_func(thread_t *t, int level, const char *fmt, ...) {
+       va_list ap;
+
+       va_start(ap, fmt);
+       lvprintf_func(t, NULL, level, fmt, ap);
+       va_end(ap);
+}
+
+
+void lprintf_ctx_func(thread_t *t, edg_wll_Context ctx, int level, const char *fmt, ...) {
+       va_list ap;
+       char *errText, *errDesc, *s;
+
+       va_start(ap, fmt);
+       edg_wll_Error(ctx, &errText, &errDesc);
+       asprintf(&s, "%s: %s", errText, errDesc);
+       lvprintf_func(t, s, level, fmt, ap);
+       free(errText);
+       free(errDesc);
+       free(s);
+       va_end(ap);
+}
+
+
+#ifdef WITH_LBU_DB
+void lprintf_dbctx_func(thread_t *t, int level, const char *fmt, ...) {
+       va_list ap;
+       char *errText = NULL, *errDesc = NULL, *s = NULL;
+       glite_lbu_DBContext dbctx = t ? t->dbctx : db.dbctx;
+
+       va_start(ap, fmt);
+       if (dbctx) {
+               glite_lbu_DBError(dbctx, &errText, &errDesc);
+               asprintf(&s, "%s: %s", errText, errDesc);
+       }
+       lvprintf_func(t, s, level, fmt, ap);
+       free(errText);
+       free(errDesc);
+       free(s);
+       va_end(ap);
+}
+#endif
+
+#ifndef WITH_LBU_DB
+time_t glite_lbu_StrToTime(const char *str) {
+       struct tm       tm;
+
+       memset(&tm,0,sizeof(tm));
+       putenv("TZ=UTC"); tzset();
+       sscanf(str,"%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);
+}
+
+double glite_lbu_StrToTimestamp(const char *str) {
+       struct tm       tm;
+       double  sec;
+
+       memset(&tm,0,sizeof(tm));
+       putenv("TZ=UTC"); tzset();
+       sscanf(str,"%4d-%02d-%02d %02d:%02d:%lf",
+               &tm.tm_year,&tm.tm_mon,&tm.tm_mday,
+               &tm.tm_hour,&tm.tm_min,&sec);
+       tm.tm_year -= 1900;
+       tm.tm_mon--;
+       tm.tm_sec = sec;
+
+       return (sec - tm.tm_sec) + mktime(&tm);
+}
+#endif
+
+
+// hacky time->string conversion
+char *time2str(thread_t *t, time_t time) {
+       struct tm tm;
+
+       if ((int)time <= 0) memcpy(t->time_s, "-", sizeof("-"));
+       else {
+               localtime_r(&time, &tm);
+               strftime(t->time_s, sizeof(t->time_s), "%F %T", &tm);
+       }
+       return t->time_s;
+}
+
+
+double rtm_gettimeofday() {
+       struct timeval tv;
+
+       gettimeofday(&tv, NULL);
+       return tv.tv_sec + tv.tv_usec / 1000000.0;
+}
+
+
+void rtm_time2str(time_t t, char **str) {
+       struct tm       *tm;
+
+       if (t) {
+               tm = gmtime(&t);
+               asprintf(str,"%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);
+       } else
+               *str = strdup("-");
+}
+
+
+void rtm_timestamp2str(double t, char **str) {
+       time_t tsec = t;
+       struct tm *tm = gmtime(&tsec);
+
+       if (t) {
+               t = t - tsec + tm->tm_sec;
+               asprintf(str,"%4d-%02d-%02d %02d:%02d:%02.09f",tm->tm_year+1900,tm->tm_mon+1,
+                        tm->tm_mday,tm->tm_hour,tm->tm_min,t);
+       } else
+               *str = strdup("-");
+}
+
+
+int rtm_str2time(const char *s) {
+       time_t t;
+
+       if (s && memcmp(s, "-", 2) != 0) {
+               t = glite_lbu_StrToTime(s);
+               if (t == (time_t)-1) return 0;
+       } else
+               t = 0;
+
+       return t;
+}
+
+
+int rtm_str2timestamp(const char *s) {
+       double t;
+
+       if (s && memcmp(s, "-", 2) != 0) {
+               t = glite_lbu_StrToTimestamp(s);
+               if (t <= 0.5) return 0.0;
+       } else
+               t = 0.0;
+
+       return t;
+}
+
+
+int rtm_str2notiftype(const char *str) {
+       if (strcasecmp(str, "STATUS") == 0) return RTM_NOTIF_TYPE_STATUS;
+       if (strcasecmp(str, "DONE") == 0) return RTM_NOTIF_TYPE_DONE;
+       if (strcasecmp(str, "JDL") == 0) return RTM_NOTIF_TYPE_JDL;
+       if (strcasecmp(str, "OLD") == 0) return RTM_NOTIF_TYPE_OLD;
+       return -1;
+}
+
+
+const char *rtm_notiftype2str(int type) {
+       switch (type) {
+       case RTM_NOTIF_TYPE_STATUS: return "STATUS";
+       case RTM_NOTIF_TYPE_DONE: return "DONE";
+       case RTM_NOTIF_TYPE_JDL: return "JDL";
+       case RTM_NOTIF_TYPE_OLD: return "OLD";
+       default: return NULL;
+       }
+}
+
+
+/**
+ * Cut the network server hostname from the full URL (got from RegJob event).
+ *
+ * Formats (only the first one should be in the wild):
+ *     https://wms2.egee.cesnet.cz:7443/glite_wms_wmproxy_server
+ *     wms2.egee.cesnet.cz
+ *     147.228.1.129
+ *     HTTPS://[2001:0f68:0000:0000:0000:0000:1986:69af]:80/
+ *     2001:0f68::1986:69af
+ */
+char* rtm_ns2hostname(const char *network_server) {
+       char *ns, *pos;
+       size_t len;
+
+       if (strncasecmp(network_server, "https://", 8) == 0) {
+               ns = strdup(network_server + 8);
+               // first backslash - path
+               pos = strchr(ns, '/');
+               if (pos) pos[0] = '\0';
+               // last colon - port separator
+               pos = strrchr(ns, ':');
+               if (pos) pos[0] = '\0';
+               // brackets - IPv6 address
+               len = strlen(ns);
+               if (len >= 2 && ns[0] == '[' && ns[len - 1] == ']') {
+                       pos = strndup(ns + 1, len - 2);
+                       free(ns);
+                       ns = pos;
+               }
+               return ns;
+       } else
+               return strdup(network_server);
+}
+
+
+void wlcg_timeval2str(struct timeval *t, char **str) {
+       struct tm       *tm;
+
+       tm = gmtime(&t->tv_sec);
+       asprintf(str,"%4d-%02d-%02dT%02d:%02d:%02dZ",tm->tm_year+1900,tm->tm_mon+1,
+               tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
+}
+
+
+int wlcg_store_message(thread_t *t, __attribute((unused))notif_t *notif, edg_wll_JobStat *stat) {
+       unsigned int port;
+       int status = 0;
+       char *jobid_str = NULL, *state_str = NULL, *vo = NULL, *lbhost = NULL, *network_host = NULL;
+       char *wlcg_last_update_time_str = NULL, *wlcg_state_start_time_str = NULL;
+
+       jobid_str = stat->jobId ? glite_jobid_unparse(stat->jobId) : strdup("Unknown");
+       glite_jobid_getServerParts(stat->jobId, &lbhost, &port);
+       state_str = edg_wll_StatToString(stat->state);
+       vo = edg_wll_JDLField(stat,"VirtualOrganisation") ? : strdup("Unknown");
+       network_host = stat->network_server ? rtm_ns2hostname(stat->network_server) : NULL;
+
+       if (!t->dash_filename || !t->dash_fd) {
+               free(t->dash_filename);
+               asprintf(&t->dash_filename, WLCG_FILENAME_TEMPLATE, t->id);
+               if ((t->dash_fd = mkstemp(t->dash_filename)) == -1) {
+                       status = errno;
+                       lprintf(t, ERR, "can't create temporary file '%s': %s", t->dash_filename, strerror(status));
+                       free(t->dash_filename);
+                       t->dash_filename = NULL;
+                       goto quit;
+               }
+       }
+
+       wlcg_timeval2str(&stat->lastUpdateTime, &wlcg_last_update_time_str);
+       wlcg_timeval2str(&stat->stateEnterTime, &wlcg_state_start_time_str);
+
+       dprintf(t->dash_fd, "jobId: %s\n\
+stateName: %s\n\
+ownerDN: %s\n\
+voname: %s\n\
+bkHost: %s:%d\n\
+networkHost: %s\n\
+lastUpdateTime: %s\n\
+stateStartTime: %s\n\
+exitCode: %d\n\
+DoneCode: %d\n\
+destSite: %s\n\
+condorId: %s\n\
+StatusReason: %s\n\
+EOT\n", jobid_str, state_str, stat->owner, vo, lbhost, port, network_host ? : "unknown", wlcg_last_update_time_str, wlcg_state_start_time_str, stat->exit_code, stat->done_code, stat->destination ? : "NULLByPublisher", stat->condorId ? : "0", stat->reason && stat->reason[strspn(stat->reason, " \t\n\r")] != '\0' ? stat->reason : "UNAVAILABLE By Publisher");
+
+       free(wlcg_last_update_time_str);
+       free(wlcg_state_start_time_str);
+quit:
+       free(jobid_str);
+       free(lbhost);
+       free(network_host);
+       free(state_str);
+       free(vo);
+       return status;
+}
+
+
+int wlcg_send_message(thread_t *t) {
+       int status = 0;
+       char *command;
+
+       // WLCG message
+       if (t->dash_fd) { // send only if anything to send
+               close(t->dash_fd);
+               asprintf(&command, "'%s' -c '%s' '%s' '%s'", config.wlcg_binary, config.wlcg_config, config.wlcg_topic, t->dash_filename);
+               lprintf(t, DBG, "calling %s", command);
+               switch (vfork()) {
+               case 0:
+                       if (execlp("/bin/sh", "/bin/sh", "-c", command, NULL) == -1) {
+                               lprintf(t, ERR, "can't exec '%s':%s", command, strerror(errno));
+                       }
+                       _exit(1);
+                       break;
+               case -1:
+                       lprintf(t, ERR, "can't fork: %s", strerror(errno));
+                       break;
+               default:
+                       break;
+               }
+               wait(&status);
+               free(command);
+               if (WIFEXITED(status)) {
+                       status = WEXITSTATUS(status);
+                       if (status) {
+                               lprintf(t, WRN, "%s exited with %d", config.wlcg_binary, status);
+                       } else {
+                               lprintf(t, DBG, "%s exited successfully", config.wlcg_binary);
+                               if (!config.wlcg_no_remove) remove(t->dash_filename);
+                       }
+               } else {
+                       lprintf(t, ERR, "%s not exited normally", config.wlcg_binary);
+                       status = -1;
+               }
+               free(t->dash_filename);
+               t->dash_filename = NULL;
+               t->dash_fd = 0;
+       }
+
+       return status;
+}
+
+
+void notif_free(notif_t *notif) {
+       edg_wll_NotifIdFree(notif->id);
+       free(notif->id_str);
+       free(notif->server);
+       memset(notif, 0, sizeof(notif_t));
+}
+
+
+void notif_invalidate(notif_t *notif) {
+       edg_wll_NotifIdFree(notif->id);
+       free(notif->id_str);
+       notif->id = NULL;
+       notif->id_str = NULL;
+       notif->error = 0;
+}
+
+
+int notif_copy(notif_t *dest, notif_t *src) {
+       if (!src || !dest) return EINVAL;
+       memset(dest, 0, sizeof(notif_t));
+       if (src->id) dest->id = edg_wll_NotifIdDup(src->id);
+       if (src->id_str) dest->id_str = strdup(src->id_str);
+       dest->type = src->type;
+       if (src->server) dest->server = strdup(src->server);
+       dest->port = src->port;
+       dest->valid = src->valid;
+       dest->refresh = src->refresh;
+       dest->last_update = src->last_update;
+       dest->active = src->active;
+       dest->error = src->error;
+       return 0;
+}
+
+
+#ifdef WITH_LBU_DB
+static int db_init(thread_t *t, glite_lbu_DBContext *dbctx) {
+       int err, dbcaps;
+
+       if (config.dbcs) {
+               if ((err = glite_lbu_InitDBContext(dbctx, GLITE_LBU_DB_BACKEND_PSQL)) != 0) {
+                       lprintf_dbctx(t, ERR, "can't initialize DB context");
+                       return err;
+               }
+               while ((err = glite_lbu_DBConnect(*dbctx, config.dbcs)) != 0 && !quit) {
+                       lprintf_dbctx(t, ERR, "can't connect to '%s'", config.dbcs);
+                       lprintf(t, INF, "still trying...");
+                       sleep(5);
+               }
+               if (err == 0) {
+                       if ((dbcaps = glite_lbu_DBQueryCaps(*dbctx)) == -1) {
+                               lprintf_dbctx(t, ERR, "can't get database capabilities");
+                               dbcaps = 0;
+                       }
+                       lprintf(t, INF, "DB connected, cs: %s, capabilities: %d", config.dbcs, dbcaps);
+                       if (t == NULL && (dbcaps & GLITE_LBU_DB_CAP_PREPARED) == 0) {
+                               lprintf(NULL, WRN, "postgresql server doesn't support SQL prepared commands, recommended version >= 8.2");
+                       }
+                       if (t) t->dbcaps = dbcaps;
+                       return 0;
+               } else {
+                       glite_lbu_FreeDBContext(*dbctx);
+                       return err;
+               }
+       } else {
+               lprintf(t, DBG, "no DB configured (--pg option)");
+               return -1;
+       }
+}
+
+
+static void db_free(__attribute((unused))thread_t *t, glite_lbu_DBContext dbctx) {
+       if (dbctx) {
+               glite_lbu_DBClose(dbctx);
+               glite_lbu_FreeDBContext(dbctx);
+       }
+}
+#endif
+
+
+static notif_t *db_add_notif(char *notifid, int type, time_t valid, time_t refresh, double last_update, char *server, int port, int active, int errors) {
+       void *tmp;
+       notif_t *notif;
+
+       if (db.n >= db.maxn) {
+               db.maxn = db.n + 20;
+               if ((tmp = realloc(db.notifs, db.maxn * sizeof(notif_t))) == NULL) return NULL;
+               db.notifs = (notif_t *)tmp;
+               memset(db.notifs + db.n, 0, (db.maxn - db.n) * sizeof(notif_t));
+       }
+       notif = db.notifs + db.n;
+       notif->id_str = notifid;
+       notif->type = type;
+       notif->valid = valid;
+       notif->refresh = refresh;
+       notif->last_update = last_update;
+       notif->server = server;
+       notif->port = port;
+       notif->active = active;
+       notif->error = errors;
+       db.n++;
+
+       return notif;
+}
+
+
+static int db_save_notifs_file(thread_t *t) {
+       FILE *f;
+       char *filename = NULL;
+       int retval = 1;
+       notif_t *notif;
+       int i;
+       char *valid_str = NULL, *refresh_str = NULL, *last_update_str = NULL, *id_str = NULL;
+
+       asprintf(&filename, "%s-new", config.notif_file);
+       if ((f = fopen(filename, "wt")) == NULL) {
+               lprintf(t, ERR, "can't write '%s': %s", filename, strerror(errno));
+               goto quit;
+       }
+
+       for (i = 0; i < db.n; i++) {
+               notif = db.notifs + i;
+               if (!notif->active) {
+                       lprintf(t, DBG, "not saving inactive notif %s (%s), server %s:%d", notif->id_str, rtm_notiftype2str(notif->type), notif->server, notif->port);
+                       continue;
+               }
+
+               if (notif->id_str) id_str = strdup(notif->id_str);
+               else if (notif->error) asprintf(&id_str, "%s:%d", notif->server, notif->port);
+               if (id_str) {
+                       rtm_time2str(notif->valid, &valid_str);
+                       rtm_time2str(notif->refresh, &refresh_str);
+                       rtm_timestamp2str(notif->last_update, &last_update_str);
+
+                       fprintf(f, RTM_FILE_NOTIF_PRINTF, id_str, rtm_notiftype2str(notif->type), valid_str, refresh_str, last_update_str, notif->error);
+
+                       free(valid_str); valid_str = NULL;
+                       free(refresh_str); refresh_str = NULL;
+                       free(last_update_str); last_update_str = NULL;
+               }
+               free(id_str);
+               id_str = NULL;
+       }
+       fclose(f);
+       if (rename(filename, config.notif_file) != 0) {
+               lprintf(t, ERR, "can't move new notification file '%s' to '%s': %s", filename, config.notif_file, strerror(errno));
+               goto quit;
+       }
+       retval = 0;
+quit:
+       free(filename);
+       free(valid_str);
+       free(refresh_str);
+       free(last_update_str);
+       return 0;
+}
+
+
+#if defined(WITH_RTM_SQL_STORAGE) && defined(WITH_LBU_DB)
+static int db_save_notifs_sql(thread_t *t) {
+       int retval = 1;
+       notif_t *notif;
+       int i;
+       char *sql = NULL, *valid_str = NULL, *refresh_str = NULL, *last_update_str = NULL;
+       const char *type_str, *amp;
+
+       for (i = 0; i < db.n; i++) {
+               notif = db.notifs + i;
+/*
+               if (!notif->active) {
+                       lprintf(t, INS, "not saving inactive notif %s (%s:%d)", notif->id_str, notif->server, notif->port);
+                       continue;
+               }
+*/
+               type_str = rtm_notiftype2str(notif->type);
+               if (notif->id_str || notif->error) {
+                       if (notif->valid) glite_lbu_TimeToDB(db.dbctx, notif->valid, &valid_str);
+                       else valid_str = strdup("NULL");
+                       if (notif->refresh) glite_lbu_TimeToDB(db.dbctx, notif->refresh, &refresh_str);
+                       else refresh_str = strdup("NULL");
+                       if (notif->last_update) glite_lbu_TimestampToDB(db.dbctx, notif->last_update, &last_update_str);
+                       else last_update_str = strdup("NULL");
+                       amp = notif->id_str ? "'" : " ";
+                       trio_asprintf(&sql, "UPDATE notifs SET notifid=%s%|Ss%s, valid=%s, refresh=%s, last_update=%s, errors=%d WHERE lb='%|Ss' AND port=%d AND notiftype='%|Ss'", amp, notif->id_str ? : "NULL", amp, valid_str, refresh_str, last_update_str, notif->error, notif->server, notif->port, type_str);
+                       switch (glite_lbu_ExecSQL(db.dbctx, sql, NULL)) {
+                       case 0:
+                               // not found - insert
+                               // can be handy when using file as input of LBs
+                               free(sql);
+                               trio_asprintf(&sql, "INSERT INTO notifs (lb, port, notifid, notiftype, valid, refresh, last_update, errors) VALUES ('%|Ss', %d, %s%|Ss%s, '%|Ss', %s, %s, %s, %d)", notif->server, notif->port, amp, notif->id_str ? : "NULL", amp, type_str, valid_str, refresh_str, last_update_str, notif->error);
+                               switch (glite_lbu_ExecSQL(db.dbctx, sql, NULL)) {
+                               case -1:
+                                       lprintf_dbctx(t, ERR, "notif '%s' (%s) insert failed", notif->id_str, type_str);
+                                       goto quit;
+                               case 0:
+                                       lprintf(t, ERR, "notif '%s' (%s) not inserted for unknown reason", type_str);
+                                       break;
+                               default:
+                                       lprintf(t, INS, "notif '%s' (%s) inserted", notif->id_str, type_str);
+                                       break;
+                               }
+                               break;
+                       case -1:
+                               lprintf_dbctx(t, ERR, "notif '%s' (%s) update failed", notif->id_str, type_str);
+                               goto quit;
+                       default:
+                               lprintf(t, INS, "notif '%s' updated", notif->id_str);
+                               break;
+                       }
+               } else {
+                       trio_asprintf(&sql, "UPDATE notifs SET notifid=NULL, valid=NULL, refresh=NULL, last_update=NULL WHERE lb='%|Ss' AND port=%d AND notiftype='%|Ss'", notif->server, notif->port, type_str);
+                       switch (glite_lbu_ExecSQL(db.dbctx, sql, NULL)) {
+                       case 0:
+                               lprintf(t, INS, "cleared %s notif for %s:%d not found, ok", type_str, notif->server, notif->port);
+                               break;
+                       case -1:
+                               lprintf_dbctx(t, ERR, "clearing notif %s for %s:%d failed", type_str, notif->server, notif->port);
+                               goto quit;
+                       default:
+                               lprintf(t, INS, "cleared notif %s for %s:%d", type_str, notif->server, notif->port);
+                               break;
+                       }
+               }
+               free(sql); sql = NULL;
+               free(valid_str); valid_str = NULL;
+               free(refresh_str); refresh_str = NULL;
+               free(last_update_str); last_update_str = NULL;
+       }
+       retval = 0;
+quit:
+       free(sql);
+       free(valid_str);
+       free(refresh_str);
+       free(last_update_str);
+       return 0;
+}
+#endif
+
+
+static int db_save_notifs(thread_t *t) {
+#if 0
+       int i;
+
+       for (i = 0; i < db.n; i++) {
+               notif_t *notif = db.notifs + i;
+               lprintf(NULL, DBG, "save: %s (%s), server: %s:%d, active: %d", notif->id_str, rtm_notiftype2str(notif->type), notif->server, notif->port, notif->active);
+       }
+#endif
+
+#if defined(WITH_LBU_DB)
+       int i, ret;
+       notif_t *notif;
+
+       //
+       // Keep monitored flag when:
+       // 1) used and opened DB
+       // 2) LB servers not from config file
+       //
+       if (t && t->dbctx && !config.config_file) {
+               for (i = 0; i < t->nservers; i++) {
+                       notif = t->notifs + i;
+
+                       if (notif->type == RTM_NOTIF_TYPE_OLD || notif->type == RTM_NOTIF_TYPE_JDL) {
+                               lprintf(t, DBG, "changing monitored flag of %d. notification for %s:%d to %d", i, notif->server, notif->port, notif->error ? 0 : 1);
+                               if ((t->dbcaps & GLITE_LBU_DB_CAP_PREPARED) == 0) {
+                                       char *sql;
+
+                                       trio_asprintf(&sql, "UPDATE " DBAMP RTM_DB_TABLE_LBS DBAMP " SET monitored=%s WHERE ip='%|Ss'", notif->error ? "false" : "true", notif->server);
+                                       ret = glite_lbu_ExecSQL(t->dbctx, sql, NULL);
+                                       free(sql);
+                               } else {
+                                       ret = glite_lbu_ExecPreparedStmt(t->updatecmd_mon, 2,
+                                               GLITE_LBU_DB_TYPE_BOOLEAN, notif->error ? 0 : 1,
+                                               GLITE_LBU_DB_TYPE_VARCHAR, notif->server
+                                       );
+                               }
+                               if (ret == -1) {
+                                       lprintf_dbctx(t, ERR, "can't update monitored flag in " RTM_DB_TABLE_LBS " table");
+                                       return 1;
+                               }
+                       }
+               }
+       }
+#endif
+
+#if defined(WITH_RTM_SQL_STORAGE) && defined(WITH_LBU_DB)
+       if (!db.dbctx) return db_save_notifs_file(t);
+       else return db_save_notifs_sql(t);
+#else
+       return db_save_notifs_file(t);
+#endif
+}
+
+
+static notif_t *db_search_notif(notif_t *notifs, int n, const char *notifid) {
+       int i;
+
+       for (i = 0; i < n && (!notifs[i].id_str || strcmp(notifs[i].id_str, notifid) != 0); i++);
+       return i == n ? NULL : notifs + i;
+}
+
+
+static notif_t *db_search_notif_by_server(notif_t *notifs, int n, const char *server, unsigned int port, int type) {
+       int i;
+
+       for (i = 0; i < n; i++) {
+               if (strcmp(notifs[i].server, server) == 0 && notifs[i].port == port && notifs[i].type == type) break;
+       }
+
+       return i == n ? NULL : notifs + i;
+}
+
+
+#ifdef WITH_LBU_DB
+typedef struct {
+       char *lb;
+       char *jobid;
+       char *unique_str;
+       char *ce;
+       char *queue;
+       char *rb;
+       char *ui;
+       char *state;
+       double state_entered;
+       double rtm_timestamp;
+       int registered;
+       char * vo;
+} db_job_t;
+
+
+//
+// store state into dababase
+// on purged status deletes the record
+//
+static void db_store_change_perform_sql(thread_t *t, edg_wll_JobStatCode state, db_job_t *rec) {
+       char *state_entered_str = NULL, *rtm_timestamp_str = NULL, *regtime_str = NULL;
+       char *sql = NULL, *sql2 = NULL, *sql_part = NULL;
+       const char *active = "true", *state_changed = "true";
+
+       if (state == EDG_WLL_JOB_PURGED) {
+               if (!config.no_purge) {
+                       lprintf(t, DBG, "purge %s", rec->jobid);
+                       if ((t->dbcaps & GLITE_LBU_DB_CAP_PREPARED) == 0) {
+                               trio_asprintf(&sql, "DELETE FROM " RTM_DB_TABLE_JOBS " WHERE jobid='%|Ss' AND lb='%|Ss'", rec->unique_str, rec->lb);
+                               lprintf(t, INS, "delete: %s", sql);
+                               if (glite_lbu_ExecSQL(t->dbctx, sql, NULL) == -1) {
+                                       lprintf_dbctx(t, WRN, "can't delete job %s", rec->jobid);
+                                       goto quit;
+                               }
+                       } else {
+                               if (glite_lbu_ExecPreparedStmt(t->deletecmd, 2,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->unique_str,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->lb
+                               ) == -1) {
+                                       lprintf_dbctx(t, WRN, "can't delete job %s", rec->jobid);
+                                       goto quit;
+                               }
+                       }
+               }
+       } else {
+               if ((t->dbcaps & GLITE_LBU_DB_CAP_PREPARED) == 0) {
+                       glite_lbu_TimestampToDB(t->dbctx, rec->state_entered, &state_entered_str);
+                       glite_lbu_TimestampToDB(t->dbctx, rec->rtm_timestamp, &rtm_timestamp_str);
+                       glite_lbu_TimeToDB(t->dbctx, rec->registered, &regtime_str);
+
+                       if (rec->vo) trio_asprintf(&sql_part, ", vo='%|Ss' ", rec->vo);
+                       else sql_part = strdup("");
+                       trio_asprintf(&sql, "UPDATE " RTM_DB_TABLE_JOBS " SET ce='%|Ss', queue='%|Ss', rb='%|Ss', ui='%|Ss', state='%|Ss', state_entered=%s, rtm_timestamp=%s, active=%s, state_changed=%s, registered=%s%sWHERE jobid='%|Ss' AND lb='%|Ss'", rec->ce, rec->queue, rec->rb, rec->ui, rec->state, state_entered_str, rtm_timestamp_str, active, state_changed, regtime_str, sql_part, rec->unique_str, rec->lb);
+                       lprintf(t, INS, "update: %s", sql);
+                       switch (glite_lbu_ExecSQL(t->dbctx, sql, NULL)) {
+                       case -1:
+                               lprintf_dbctx(t, ERR, "can't get jobs");
+                               goto quit;
+                       case 0:
+                               trio_asprintf(&sql2, "INSERT INTO " RTM_DB_TABLE_JOBS " "
+                                       "(ce, queue, rb, ui, state, state_entered, rtm_timestamp, jobid, lb, active, state_changed, registered, vo) VALUES "
+                                       "('%|Ss', '%|Ss', '%|Ss', '%|Ss', '%|Ss', %s, %s, '%|Ss', '%|Ss', %s, %s, %s, '%|Ss')", rec->ce, rec->queue, rec->rb, rec->ui, rec->state, state_entered_str, rtm_timestamp_str, rec->unique_str, rec->lb, active, state_changed, regtime_str, rec->vo ? : "unknown");
+                               lprintf(t, INS, "insert: %s", sql2);
+                               if (glite_lbu_ExecSQL(t->dbctx, sql2, NULL) == -1) {
+                                       lprintf_dbctx(t, ERR, "can't insert job");
+                                       goto quit;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               } else { // prepared commands
+                       int ret;
+
+                       if (rec->vo) {
+                               ret = glite_lbu_ExecPreparedStmt(t->updatecmd_vo, 13,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->ce,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->queue,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->rb,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->ui,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->state,
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, rec->state_entered,
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, rec->rtm_timestamp,
+                                       GLITE_LBU_DB_TYPE_BOOLEAN, 1, // active
+                                       GLITE_LBU_DB_TYPE_BOOLEAN, 1, // state_changed
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, (double)rec->registered,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->vo, // VO
+
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->unique_str, // jobid
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->lb // L&B server
+                               );
+                       } else {
+                               ret = glite_lbu_ExecPreparedStmt(t->updatecmd, 12,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->ce,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->queue,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->rb,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->ui,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->state,
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, rec->state_entered,
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, rec->rtm_timestamp,
+                                       GLITE_LBU_DB_TYPE_BOOLEAN, 1, // active
+                                       GLITE_LBU_DB_TYPE_BOOLEAN, 1, // state_changed
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, (double)rec->registered,
+
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->unique_str, // jobid
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->lb // L&B server
+                               );
+                       }
+
+                       switch (ret) {
+                       case -1:
+                               lprintf_dbctx(t, ERR, "can't update " RTM_DB_TABLE_JOBS " table");
+                               goto quit;
+                       case 0:
+                               if (glite_lbu_ExecPreparedStmt(t->insertcmd, 13,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->ce,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->queue,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->rb,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->ui,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->state,
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, rec->state_entered,
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, rec->rtm_timestamp,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->unique_str, // jobid
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->lb, // L&B server
+                                       GLITE_LBU_DB_TYPE_BOOLEAN, 1, // active
+                                       GLITE_LBU_DB_TYPE_BOOLEAN, 1, // state_changed
+                                       GLITE_LBU_DB_TYPE_TIMESTAMP, (double)rec->registered,
+                                       GLITE_LBU_DB_TYPE_VARCHAR, rec->vo ? : "unknown" // VO
+                               ) == -1) {
+                                       lprintf_dbctx(t, ERR, "can't insert to " RTM_DB_TABLE_JOBS " table");
+                                       goto quit;
+                               }
+                               break;
+                       default:
+                               break;
+                       }
+               } // prepare commands
+       }
+
+quit:
+       free(sql);
+       free(sql2);
+       free(sql_part);
+       free(state_entered_str);
+       free(rtm_timestamp_str);
+       free(regtime_str);
+}
+#endif
+
+
+static int db_store_change(thread_t *t, notif_t *notif, __attribute((unused))int index, edg_wll_JobStat *stat) {
+       char *jobid_str = NULL, *state_str = NULL, *vo = NULL, *lbhost = NULL;
+       unsigned int port;
+
+       jobid_str = stat->jobId ? glite_jobid_unparse(stat->jobId) : strdup("unknown");
+       glite_jobid_getServerParts(stat->jobId, &lbhost, &port);
+       state_str = edg_wll_StatToString(stat->state);
+       vo = edg_wll_JDLField(stat, "VirtualOrganisation");
+       printf(RTM_TTY_GREEN "notifid: %s (%s), jobid: %s, state: %s, vo: %s, last time: %lf" RTM_TTY_RST "\n", notif->id_str, rtm_notiftype2str(notif->type), jobid_str, state_str, vo, notif->last_update);
+
+#ifdef WITH_LBU_DB
+       if (config.dbcs && t->dbctx) {
+               db_job_t rec;
+               char *colon;
+               char *unique_str = NULL, *network_server = NULL;
+
+               memset(&rec, 0, sizeof rec);
+               // L&B server
+               rec.lb = lbhost;
+               // jobid + uniqe
+               unique_str = glite_jobid_getUnique(stat->jobId);
+               rec.unique_str = unique_str;
+               rec.jobid = jobid_str;
+               // CE
+               rec.ce = stat->destination ? : "unknown";
+               // queue
+               rec.queue = strchr(rec.ce, '/');
+               if (rec.queue) *rec.queue++='\0';
+               else rec.queue = "unknown";
+               colon = strchr(rec.ce, ':');
+               if (colon) colon[0] = '\0';
+               // Virtual Organization
+               rec.vo = vo;
+               // Resource Broker
+               network_server = stat->network_server ? rtm_ns2hostname(stat->network_server) : strdup("unknown");
+               rec.rb = network_server;
+               // UI
+               rec.ui = stat->ui_host ? : "unknown";
+               // state
+               rec.state = state_str ? : "unknown";
+               // state time
+               rec.state_entered = stat->stateEnterTime.tv_sec + stat->stateEnterTime.tv_usec / 1000000.0;
+               // notification time
+               rec.rtm_timestamp = rtm_gettimeofday();
+               // registration time
+               rec.registered = stat->stateEnterTimes[1 + EDG_WLL_JOB_SUBMITTED];
+
+               // store!
+               db_store_change_perform_sql(t, stat->state, &rec);
+
+               free(unique_str);
+               free(network_server);
+       }
+#endif
+
+       // store message
+       if (config.wlcg) {
+               if (wlcg_store_message(t, notif, stat) != 0) goto quit;
+               if (config.wlcg_flush) wlcg_send_message(t);
+       }
+
+quit:
+       free(jobid_str);
+       free(state_str);
+       free(lbhost);
+       free(vo);
+
+       return 0;
+}
+
+
+static int db_summary_getjobids(__attribute((unused))db_t *db, __attribute((unused))int maxn, __attribute((unused))char **jobids, int *n) {
+/*
+       switch (db->was_summary) {
+       case 0:
+               *n = 3;
+               jobids[0] = strdup("https://skurut68-2.cesnet.cz:9000/FJldtiAR2EHC12C3Zz8WjQ");
+               jobids[1] = strdup("https://skurut68-2.cesnet.cz:9000/AWTCWrUCr3uUh6cuRFaENQ");
+               jobids[2] = strdup("https://skurut68-1.cesnet.cz:9000/o73CG2wrNdEQ909mG0Ac1g");
+               break;
+       case 1:
+               *n = 1;
+               jobids[0] = strdup("https://skurut68-2.cesnet.cz:9000/-46Qa2ag4gLsA_Ki-3bSLw");
+
+               break;
+       default: *n = 0; break;
+       }
+       db->was_summary = (db->was_summary + 1) % 3;
+       return 0;
+*/
+       *n = 0;
+       return 0;
+}
+
+
+static int db_summary_setinfo(__attribute((unused))db_t *db, edg_wll_JobStat *stat) {
+       char *jobidstr;
+
+       jobidstr = stat->jobId ? glite_jobid_unparse(stat->jobId) : NULL;
+       printf(RTM_TTY_GREEN "summary: jobid='%s'" RTM_TTY_RST "\n", jobidstr);
+       free(jobidstr);
+       return 0;
+}
+
+
+int rtm_summary(edg_wll_Context ctx, db_t *db) {
+       char *jobids[RTM_SUMMARY_JOBS];
+       edg_wll_QueryRec lbquery[RTM_SUMMARY_JOBS + 1], *qr;
+       const edg_wll_QueryRec *lbqueryext[2];
+       edg_wll_JobStat *jobstates = NULL;
+       int err = 0, ijob = 0, njobs = 0, iquery = 0, k, server_changed = 0;
+       glite_jobid_t jid = NULL;
+       char *server = NULL, *new_server = NULL;
+       unsigned int port = 0, new_port = 0;
+
+       lprintf(NULL, INS, "Summary");
+
+       lbqueryext[0] = lbquery;
+       lbqueryext[1] = NULL;
+       memset(lbquery, 0, sizeof(lbquery));
+
+       do {
+               if (server) {
+
+               if ((iquery >= RTM_SUMMARY_JOBS || server_changed || !njobs) && iquery) {
+                       if ((err = edg_wll_QueryJobsExt(ctx, lbqueryext, 0, NULL, &jobstates)) != 0) {
+                               lprintf_ctx(NULL, ERR, ctx, "query to '%s:%u' failed: %s", server, port, strerror(err));
+                               // report error jobids and skip the job (do nothing)
+                               // TODO
+                       }
+                       for (k = 0; k < iquery; k++) glite_jobid_free((glite_jobid_t)lbquery[k].value.j);
+
+                       if (err == 0) {
+                               for (k = 0; jobstates[k].state != EDG_WLL_JOB_UNDEF; k++) {
+                                       if ((err = db_summary_setinfo(db, jobstates + k)) != 0) lprintf(NULL, ERR, "Can't store %d. summary info for %s:%u", k, server, port);
+                                       edg_wll_FreeStatus(jobstates + k);
+                               }
+                               free(jobstates);
+                               lprintf(NULL, DBG, "query to '%s:%u' succeed", server, port);
+                       }
+
+                       iquery = 0;
+                       memset(lbquery, 0, sizeof(lbquery));
+                       if (!njobs) break; // not needed, just spare summary select
+
+                       server_changed = 0;
+               } else {
+                       lprintf(NULL, DBG, "summary pushed %d. %s\n", iquery, jobids[ijob]);
+                       qr = lbquery + iquery;
+                       iquery++;
+                       qr->attr = EDG_WLL_QUERY_ATTR_JOBID;
+                       qr->op = EDG_WLL_QUERY_OP_EQUAL;
+                       glite_jobid_parse(jobids[ijob], (glite_jobid_t *)&qr->value.j);
+                       free(jobids[ijob]); jobids[ijob] = NULL;
+                       ijob++;
+               }
+
+               } // server
+
+               if (ijob >= njobs) {
+                       ijob = 0;
+                       memset(jobids, 0, sizeof(jobids));
+                       njobs = 0;
+                       if ((err = db_summary_getjobids(db, RTM_SUMMARY_JOBS, jobids, &njobs)) != 0) {
+                               lprintf(NULL, ERR, "Can't get jobs for the summary");
+                               return err;
+                       }
+                       lprintf(NULL, DBG, "summary for %d jobs", njobs);
+                       if (!njobs) {
+                               if (iquery) continue; // do the last query
+                               else break;
+                       }
+               }
+
+               if ((err = glite_jobid_parse(jobids[ijob], &jid)) != 0) {
+                       lprintf(NULL, ERR, "Can't parse jobid '%s': %s", jobids[ijob], strerror(err));
+                       // report error jobid and skip the job
+                       // TODO
+                       glite_jobid_free(jid); jid = NULL;
+                       free(jobids[ijob]); jobids[ijob] = NULL;
+                       ijob++;
+                       continue;
+               }
+               free(new_server);
+               glite_jobid_getServerParts(jid, &new_server, &new_port);
+               glite_jobid_free(jid); jid = NULL;
+
+               // first or different LB server
+               if (new_server && (!server || strcmp(server, new_server) != 0 || port != new_port)) {
+                       if (server) server_changed = 1;
+
+                       free(server);
+                       server = new_server;
+                       port = new_port;
+
+                       new_server = NULL;
+                       new_port = 0;
+
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER, server);
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER_PORT, port);
+                       lprintf(NULL, INF, "summary LB server '%s:%u'", server, port);
+               }
+       } while (njobs || iquery);
+
+       free(server);
+       free(new_server);
+
+       return err;
+}
+
+
+/*
+ * Updates error counter and retry times on the notification.
+ *
+ * On errors it lineary increases delay. Minimum delay is
+ * RTM_ERROR_REPEAT_RATE, maximum is half of the configured
+ * bootstrap time.
+ *
+ * \param t            thread context
+ * \param notif                updated notification
+ * \param[IN] index    notification order (for debug printing)
+ * \param is_error[IN] error state (to reset or increment error counter)
+ *
+ */
+static int rtm_update_error_state(thread_t *t, notif_t *notif, int index, int is_error) {
+       int old_error, max_count;
+
+       old_error = notif->error;
+       if (is_error) {
+               if (!notif->error++ || !notif->refresh) notif->refresh = time(NULL);
+               max_count = config.dive / RTM_ERROR_REPEAT_RATE / 2;
+               if (max_count <= 0) max_count = 1;
+               notif->refresh += (notif->error <= max_count ? notif->error : max_count) * RTM_ERROR_REPEAT_RATE;
+               lprintf(t, DBG, "planned to retry at %s", time2str(t, notif->refresh));
+       } else {
+               notif->error = 0;
+       }
+       if (old_error != notif->error) {
+               lprintf(t, DBG, "error count of %d. server %s:%d changed from %d to %d", index, notif->server, notif->port, old_error, notif->error);
+       }
+
+       return 0;
+}
+
+
+/**
+ * Updates notifications in persistent storage. Used to send WLCG messages too.
+ *
+ * \param t    thread context
+ * \param[IN]  new_notif updating notification, NULL = no change in shared memory
+ * \param[IN]  store     0=light (just shared memory), 1=save (flush, really store)
+ * \retval 0 if OK
+ */
+int rtm_update_notif(thread_t *t, notif_t *new_notif, int store) {
+       notif_t *notif;
+       int retval = 1;
+
+       pthread_mutex_lock(&db.lock);
+
+       if (new_notif) {        
+               if ((notif = db_search_notif_by_server(db.notifs, db.n, new_notif->server, new_notif->port, new_notif->type)) == NULL) {
+                       if (db_add_notif(strdup(new_notif->id_str), new_notif->type, new_notif->valid, new_notif->refresh, new_notif->last_update, strdup(new_notif->server), new_notif->port, 1, 0) == NULL) {
+                               lprintf(t, ERR, "can't realloc");
+                               goto quit;
+                       }
+               } else {
+                       notif_free(notif);
+                       notif_copy(notif, new_notif);
+               }
+       }
+
+       wlcg_send_message(t);
+
+       if (store) {
+               if (db_save_notifs(t) != 0) goto quit;
+       }
+       retval = 0;
+       
+quit:
+       pthread_mutex_unlock(&db.lock);
+       return retval;
+}
+
+
+int rtm_drop_notif(thread_t *t, char *notifid, int store) {
+       notif_t *notif;
+       int retval = 1;
+
+       pthread_mutex_lock(&db.lock);
+       if ((notif = db_search_notif(db.notifs, db.n, notifid)) != NULL) {
+               notif_invalidate(notif);
+               if (store)
+                       if (db_save_notifs(t) != 0) goto quit;
+       }
+       retval = 0;
+quit:
+       pthread_mutex_unlock(&db.lock);
+       return retval;
+}
+
+
+int load_notifs_file() {
+       FILE *f;
+       char *results[5];
+       notif_t *new_notif;
+       int err;
+       char *notifidstr;
+       time_t valid, refresh;
+       double last_update;
+       edg_wll_NotifId id;
+       int type, i, errcnt, port;
+       int retval = 1;
+
+       if ((f = fopen(config.notif_file, "rt")) == NULL) {
+               lprintf(NULL, WRN, "WARNING: can't open notification file '%s'", config.notif_file);
+               return 0;
+       }
+
+       results[0] = malloc(RTM_FILE_NOTIF_NUM * 512);
+       for (i = 1; i < RTM_FILE_NOTIF_NUM; i++) {
+               results[i] = results[0] + i * 512;
+       }
+       while ((err = fscanf(f, RTM_FILE_NOTIF_SCANF, results[0], results[1], results[2], results[3], results[4], results[5])) == RTM_FILE_NOTIF_NUM) {
+               notifidstr = results[0];
+               if ((type = rtm_str2notiftype(results[1])) == -1) {
+                       lprintf(NULL, ERR, "unknown notification type '%s' in '%s'", results[1], notifidstr);
+                       continue;
+               }
+
+               valid = rtm_str2time(results[2]);
+               refresh = rtm_str2time(results[3]);
+               last_update = rtm_str2timestamp(results[4]);
+
+               errcnt = 0;
+               if (results[5] && strcasecmp(results[5], "-") != 0) {
+                       errcnt = atoi(results[5]);
+               }
+
+               if (errcnt) {
+                       if (sscanf(notifidstr, "%511[^:]:%d", results[1], &port) != 2) {
+                               lprintf(NULL, WRN, "can't parse server specification '%s'", notifidstr);
+                               continue;
+                       }
+                       if ((new_notif = db_add_notif(NULL, type, valid, refresh, last_update, strdup(results[1]), port, 0, errcnt)) == NULL) {
+                               lprintf(NULL, ERR, "can't alloc");
+                               goto quit;
+                       }
+               } else {
+                       if (edg_wll_NotifIdParse(notifidstr, &id) != 0) {
+                               lprintf(NULL, WRN, "can't parse notification ID '%s'", notifidstr);
+                               continue;
+                       }
+                       if ((new_notif = db_add_notif(strdup(notifidstr), type, valid, refresh, last_update, NULL, 0, 0, errcnt)) == NULL) {
+                               lprintf(NULL, ERR, "can't alloc");
+                               goto quit;
+                       }
+                       edg_wll_NotifIdGetServerParts(id, &new_notif->server, &new_notif->port);
+                       edg_wll_NotifIdFree(id);
+               }
+       }
+       if (err == EOF) retval = 0;
+       else lprintf(NULL, ERR, "can't parse notification file '%s'", config.notif_file);
+quit:
+       fclose(f);
+       free(results[0]);
+       return retval;
+}
+
+
+#if defined(WITH_RTM_SQL_STORAGE) && defined(WITH_LBU_DB)
+int load_notifs_sql() {
+       notif_t *new_notif;
+       int err;
+       char *notifidstr;
+       time_t valid, refresh;
+       double last_update;
+       edg_wll_NotifId id;
+       int type, i, errcnt;
+       int retval = 1;
+       glite_lbu_Statement stmt = NULL;
+       char *results[8];
+
+       if (glite_lbu_ExecSQL(db.dbctx, "SELECT notifid, notiftype, valid, refresh, last_update, errors, lb, port FROM notifs", &stmt) == -1) {
+               lprintf_dbctx(NULL, ERR, "fetching notification failed");
+               goto quit;
+       }
+       while ((err = glite_lbu_FetchRow(stmt, 8, NULL, results)) > 0) {
+               if (results[0] && results[0][0]) notifidstr = strdup(results[0]);
+               else notifidstr = NULL;
+               free(results[0]);
+               results[0] = NULL;
+
+               if ((type = rtm_str2notiftype(results[1])) == -1) {
+                       lprintf(NULL, ERR, "unknown notification type '%s' in '%s'", results[1], notifidstr);
+                       for (i = 0; i < 8; i++) free(results[i]);
+                       free(notifidstr);
+                       continue;
+               }
+               free(results[1]);
+
+               valid = 0;
+               if (results[2] && results[2][0]) {
+                       valid = glite_lbu_DBToTime(db.dbctx, results[2]);
+               }
+               free(results[2]);
+
+               refresh = 0;
+               if (results[3] && results[3][0]) {
+                       refresh = glite_lbu_DBToTime(db.dbctx, results[3]);
+               }
+               free(results[3]);
+
+               last_update = 0;
+               if (results[4] && results[4][0]) {
+                       last_update = glite_lbu_DBToTimestamp(db.dbctx, results[4]);
+               }
+               free(results[4]);
+
+               errcnt = 0;
+               if (results[5] && results[5][0]) errcnt = atoi(results[5]);
+               free(results[5]);
+
+               if ((new_notif = db_add_notif(notifidstr, type, valid, refresh, last_update, (results[6] && !notifidstr) ? strdup(results[6]) : NULL, atoi(results[7]), 0, errcnt)) == NULL) {
+                       free(notifidstr);
+                       free(results[6]);
+                       free(results[7]);
+                       lprintf(NULL, ERR, "can't alloc");
+                       goto quit;
+               }
+               free(results[6]);
+               free(results[7]);
+               if (notifidstr) {
+                       if (edg_wll_NotifIdParse(notifidstr, &id) != 0) {
+                               lprintf(NULL, WRN, "can't parse notification IDs '%s'", notifidstr);
+                               notif_free(new_notif);
+                               db.n--;
+                               continue;
+                       }
+                       edg_wll_NotifIdGetServerParts(id, &new_notif->server, &new_notif->port);
+                       edg_wll_NotifIdFree(id);
+               }
+       }
+       if (err == 0) retval = 0;
+       else lprintf_dbctx(NULL, ERR, "fetching failed");
+quit:
+       if (stmt) glite_lbu_FreeStmt(&stmt);
+       return retval;
+}
+#endif
+
+
+int load_notifs() {
+       int i, ret;
+
+       pthread_mutex_lock(&db.lock);
+
+#if defined(WITH_RTM_SQL_STORAGE) && defined(WITH_LBU_DB)
+       if (!db.dbctx) ret = load_notifs_file();
+       else ret = load_notifs_sql();
+#else
+       ret = load_notifs_file();
+#endif
+       // try to reconnect on bad notifications immediately
+       for (i = 0; i < db.n; i++)
+               if (db.notifs[i].error) db.notifs[i].refresh = 0;
+
+       pthread_mutex_unlock(&db.lock);
+
+       return ret;
+}
+
+
+void db_free_notifs() {
+       int i;
+
+       for (i = 0; i < db.n; i++) notif_free(db.notifs + i);
+       free(db.notifs);
+       db.notifs = NULL;
+       db.n = db.maxn = 0;
+}
+
+
+void *notify_thread(void *thread_data) {
+       struct sockaddr_in addr;
+       int i, j, err;
+       time_t now, bootstrap;
+       edg_wll_NotifId notifid;
+       struct timeval to;
+       edg_wll_JobStat jobstat, *jobstates;
+       notif_t *notif, *notif_jdl;
+       edg_wll_QueryRec *conditions[3] = { NULL, NULL, NULL }, condition[2], condition2[2];
+       int sock = -1, updated = 0, error = 0, received = 0;
+       thread_t *t = (thread_t *)thread_data;
+       edg_wll_Context ctx = NULL;
+       int flags = 0;
+
+       const int       one = 1;
+
+       lprintf(t, DBG, "thread started");
+
+       if (!t->nservers) goto exit;
+
+       // LB
+       if (edg_wll_InitContext(&ctx) != 0) {
+               lprintf(t, ERR, "can't init LB context: %s", strerror(errno));
+               goto exit;
+       }
+       if (config.cert) edg_wll_SetParam(ctx, EDG_WLL_PARAM_X509_CERT, config.cert);
+       if (config.key) edg_wll_SetParam(ctx, EDG_WLL_PARAM_X509_KEY, config.key);
+
+       // socket
+       if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
+               lprintf(t, ERR, "can't create socket: %s", strerror(errno));
+               goto exit;
+       }
+       lprintf(t, DBG, "socket created: %d", sock);
+
+       setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
+
+       memset(&addr, 0, sizeof addr);
+       addr.sin_family = AF_INET;
+       if (listen_port) addr.sin_port = htons(listen_port + t->id);
+       addr.sin_addr.s_addr = INADDR_ANY;
+       if (bind(sock, (const struct sockaddr*)&addr, sizeof addr) != 0) {
+               lprintf(t, ERR, "can't bind socket: %s, port = %d", strerror(errno), listen_port ? listen_port + t->id : -1);
+               goto exit;
+       }
+       if (listen(sock, 10) != 0) {
+               lprintf(t, ERR, "can't listen on socket: %s", strerror(errno));
+               goto exit;
+       }
+
+#ifdef WITH_LBU_DB
+       if (db_init(t, &t->dbctx) == 0)
+               if ((t->dbcaps & GLITE_LBU_DB_CAP_PREPARED) != 0) {
+                       if (glite_lbu_PrepareStmt(t->dbctx, "INSERT INTO " DBAMP RTM_DB_TABLE_JOBS DBAMP " "
+                           "(ce, queue, rb, ui, state, state_entered, rtm_timestamp, jobid, lb, active, state_changed, registered, vo)"
+                           " VALUES "
+                           "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
+                       &t->insertcmd) != 0 || glite_lbu_PrepareStmt(t->dbctx, "UPDATE " DBAMP RTM_DB_TABLE_JOBS DBAMP " "
+                           "SET ce=$1, queue=$2, rb=$3, ui=$4, state=$5, state_entered=$6, rtm_timestamp=$7, active=$8, state_changed=$9, registered=$10 WHERE jobid=$11 AND lb=$12", 
+                       &t->updatecmd) != 0 || glite_lbu_PrepareStmt(t->dbctx, "UPDATE " DBAMP RTM_DB_TABLE_JOBS DBAMP " "
+                           "SET ce=$1, queue=$2, rb=$3, ui=$4, state=$5, state_entered=$6, rtm_timestamp=$7, active=$8, state_changed=$9, registered=$10, vo=$11 WHERE jobid=$12 AND lb=$13", 
+                       &t->updatecmd_vo) != 0 || glite_lbu_PrepareStmt(t->dbctx, "UPDATE " DBAMP RTM_DB_TABLE_LBS DBAMP " "
+                           "SET monitored=$1 WHERE ip=$2",
+                       &t->updatecmd_mon) != 0 || glite_lbu_PrepareStmt(t->dbctx, "DELETE FROM " DBAMP RTM_DB_TABLE_JOBS DBAMP " WHERE jobid=$1 AND lb=$2",
+                       &t->deletecmd) != 0) {
+                               lprintf_dbctx(t, ERR, "can't create prepare commands");
+                               lprintf(t, DBG, "insertcmd=%p, updatecmd=%p, updatecmd_vo=%p, updatecmd_mon=%p, deletecmd=%p", t->insertcmd, t->updatecmd, t->updatecmd_vo, t->updatecmd_mon, t->deletecmd);
+                               quit = RTM_QUIT_PRESERVE;
+                       }
+               }
+#endif
+
+       //
+       // notifications loop:
+       //   - refresh/create with bootstrap
+       //   - receive & store changes
+       //
+       while (!quit) {
+               now = time(NULL);
+               t->next_refresh = now + RTM_NOTIF_LOOP_MAX_TIME;
+               for (i = 0; i < t->nservers; i++) {
+                       notif = t->notifs + i;
+                       if (!notif->active) {
+                               lprintf(t, INS, "inactive %d. notification '%s' (%s)", i, notif->id_str, rtm_notiftype2str(notif->type));
+                               continue;
+                       }
+                       // skip invalid LBs if not planned yet
+                       if (notif->error) {
+                               if (notif->refresh > now) {
+                                       lprintf(t, INS, "not planned to retry previously failed %d. notification '%s' (%s), plan %s", i, notif->id_str, rtm_notiftype2str(notif->type), time2str(t, notif->refresh));
+                                       if (t->next_refresh > notif->refresh) t->next_refresh = notif->refresh;
+                                       continue;
+                               }
+                               lprintf(t, DBG, "retry previously failed %d. notification '%s' (%s)", i, notif->id_str, rtm_notiftype2str(notif->type));
+                       }
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER, notif->server);
+                       edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER_PORT, notif->port);
+                       now = time(NULL);
+                       if (!notif->valid || notif->valid - RTM_NOTIF_TTL_TO_DEAD <= now || !notif->id_str) {
+                       // new notification
+                               lprintf(t, DBG, "host %s:%d, valid %s, notifstr '%s', notifid %p", notif->server, notif->port, time2str(t, notif->valid), notif->id_str, notif->id);
+
+                               // crazy inter-notif interactions
+                               switch (notif->type) {
+                               case RTM_NOTIF_TYPE_STATUS:
+                                       // STATUS must wait for existing JDL notification
+                                       notif_jdl = db_search_notif_by_server(t->notifs, t->nservers, notif->server, notif->port, RTM_NOTIF_TYPE_JDL);
+                                       if (!notif_jdl || !notif_jdl->valid || notif_jdl->valid - RTM_NOTIF_TTL_TO_DEAD <= now || !notif_jdl->id_str) {
+                                               lprintf(t, DBG, "not created %d. notification for %s:%d (%s), waiting for %d. (JDL)", i, notif->server, notif->port, rtm_notiftype2str(notif->type), i + RTM_NOTIF_TYPE_JDL - RTM_NOTIF_TYPE_STATUS);
+                                               // next retry of STATUS stright before the JDL
+                                               if (notif_jdl) {
+                                                       notif->refresh = notif_jdl->refresh;
+                                                       if (t->next_refresh > notif->refresh) t->next_refresh = notif->refresh;
+                                               }
+                                               continue;
+                                       }
+                                       break;
+                               default:
+                                       break;
+                               }
+                               bootstrap = notif->valid > RTM_NOTIF_TTL_TO_DEAD ? notif->valid - RTM_NOTIF_TTL_TO_DEAD : 0;
+                               if (config.dive > 0 && now - bootstrap > config.dive) {
+                                       bootstrap = now - config.dive;
+                                       lprintf(t, INS, "dive from %s:%d cut to %s (max. dive %d)", notif->server, notif->port, time2str(t, bootstrap), config.dive);
+                               }
+                               // explicitly drop old (failed) notification, if any
+                               if (notif->id_str) {
+                                       if (notif->id) {
+                                               if (edg_wll_NotifDrop(ctx, notif->id)) lprintf_ctx(t, WRN, ctx, "dropping %d. notification '%s' (%s) failed", i, notif->id_str, rtm_notiftype2str(notif->type));
+                                       }
+                                       // remove from the persistent storage now,
+                                       // invalidate && update 
+                                       rtm_drop_notif(t, notif->id_str, 1);
+                                       // free the notification in the current thread
+                                       notif_invalidate(notif);
+                                       now = time(NULL);
+                               }
+                               // create the new notification
+                               notif->valid = now + config.ttl;
+
+                               memset(conditions, 0, sizeof(conditions));
+                               memset(condition, 0, sizeof(condition));
+                               memset(condition2, 0, sizeof(condition2));
+                               flags = 0;
+                               switch(notif->type) {
+#ifndef WITH_OLD_LB
+                               case RTM_NOTIF_TYPE_STATUS:
+                                       conditions[0] = condition;
+                                       condition[0].attr = EDG_WLL_QUERY_ATTR_STATUS;
+                                       condition[0].op = EDG_WLL_QUERY_OP_CHANGED;
+                                       break;
+                               case RTM_NOTIF_TYPE_JDL:
+                                       conditions[0] = condition;
+                                       conditions[1] = condition2;
+                                       condition[0].attr = EDG_WLL_QUERY_ATTR_STATUS;
+                                       condition[0].op = EDG_WLL_QUERY_OP_EQUAL;
+                                       condition[0].value.i = EDG_WLL_JOB_WAITING;
+                                       condition2[0].attr = EDG_WLL_QUERY_ATTR_JDL_ATTR;
+                                       condition2[0].op = EDG_WLL_QUERY_OP_CHANGED;
+                                       flags = EDG_WLL_STAT_CLASSADS;
+                                       break;
+#endif
+                               case RTM_NOTIF_TYPE_OLD:
+                                       flags = EDG_WLL_STAT_CLASSADS;
+                                       break;
+                               case RTM_NOTIF_TYPE_DONE:
+                                       conditions[0] = condition;
+                                       condition[0].attr = EDG_WLL_QUERY_ATTR_STATUS;
+                                       condition[0].op = EDG_WLL_QUERY_OP_EQUAL;
+                                       condition[0].value.i = EDG_WLL_JOB_DONE;
+                                       flags = EDG_WLL_STAT_CHILDREN;
+                                       break;
+                               default:
+                                       assert(notif->type != notif->type); // unknown type
+                                       break;
+                               }
+                               if (edg_wll_NotifNew(ctx, (edg_wll_QueryRec const * const *) conditions, flags, sock, config.local_address, &notif->id, &notif->valid)) {
+                                       memset(condition,0,sizeof condition);
+                                       lprintf_ctx(t, ERR, ctx, "can't create notification on %s:%d", notif->server, notif->port);
+                                       notif->valid = 0;
+                                       notif->id = NULL;
+                                       rtm_update_error_state(t, notif, i, 1);
+                                       error = 1;
+                                       goto cont;
+                               } 
+                               notif->id_str = edg_wll_NotifIdUnparse(notif->id);
+                               lprintf(t, INF, "created %d. notification '%s' (%s), valid: %s", i, notif->id_str, rtm_notiftype2str(notif->type), time2str(t, notif->valid));
+
+                               // bootstrap
+                               memset(condition, 0, sizeof(condition));
+                               flags = 0;
+                               switch (notif->type) {
+                               case RTM_NOTIF_TYPE_STATUS:
+                                       condition[0].attr = EDG_WLL_QUERY_ATTR_LASTUPDATETIME;
+                                       condition[0].op = EDG_WLL_QUERY_OP_WITHIN;
+                                       condition[0].value.t.tv_sec = bootstrap;
+                                       condition[0].value2.t.tv_sec = now;
+                                       flags = EDG_WLL_STAT_CLASSADS;
+                                       break;
+                               case RTM_NOTIF_TYPE_OLD:
+                                       break;
+                               case RTM_NOTIF_TYPE_JDL:
+                                       break;
+                               case RTM_NOTIF_TYPE_DONE:
+                                       break;
+                               default:
+                                       assert(notif->type != notif->type); // unknown type
+                                       break;
+                               }
+
+                               if (condition[0].attr) {
+
+                               lprintf(t, INF, "bootstrap %s:%d (%d), time %s..%d(now)", notif->server, notif->port, i, time2str(t, bootstrap), now);
+                               edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER, notif->server);
+                               edg_wll_SetParam(ctx, EDG_WLL_PARAM_QUERY_SERVER_PORT, notif->port);
+                               if ((err = edg_wll_QueryJobs(ctx, condition, flags, NULL, &jobstates)) != 0 && err != ENOENT) {
+                                       lprintf_ctx(t, ERR, ctx, "can't bootstrap jobs on %s:%d, time %s..%d(now)", notif->server, notif->port, time2str(t, bootstrap), now);
+                                       //
+                                       // destroy the notification after failed bootstrap
+                                       //
+                                       // This error means there is something nasty on the remote LB server.
+                                       // It could lost some messages between recreating notification,
+                                       // so destroy this notification now.
+                                       //
+                                       if (edg_wll_NotifDrop(ctx, notif->id)) {
+                                               lprintf_ctx(t, WRN, ctx, "dropping %d. notification '%s' (%s) after failed bootstrap failed", i, notif->id_str, rtm_notiftype2str(notif->type));
+                                       } else {
+                                               lprintf(t, INF, "dropped %d. notification '%s' (%s) after failed bootstrap", i, notif->id_str, rtm_notiftype2str(notif->type));
+                                       }
+                                       // free the notification instance in the current thread
+                                       // (not propagated to the persistent storage yet)
+                                       edg_wll_NotifIdFree(notif->id);
+                                       notif->id = NULL;
+                                       free(notif->id_str);
+                                       notif->id_str = NULL;
+                                       notif->valid = 0;
+                                       rtm_update_error_state(t, notif, i, 1);
+                                       error = 1;
+                                       if (t->next_refresh > notif->refresh) t->next_refresh = notif->refresh;
+                                       goto cont;
+                               } else {
+                                       for (j = 0; jobstates[j].state != EDG_WLL_JOB_UNDEF; j++) {
+                                               notif->last_update = jobstates[j].lastUpdateTime.tv_sec + jobstates[j].lastUpdateTime.tv_usec / 1000000.0;
+                                               db_store_change(t, notif, i, jobstates + j);
+                                               edg_wll_FreeStatus(jobstates + j);
+                                       }
+                                       free(jobstates);
+                                       lprintf(t, INF, "bootstrap %s:%d (%d), found %d jobs", notif->server, notif->port, i, j);
+                                       rtm_update_error_state(t, notif, i, 0);
+                                       updated = 1;
+                               }
+
+                               } else {
+                                       rtm_update_error_state(t, notif, i, 0);
+                                       updated = 1;
+                               }
+                       } else if (!notif->id) {
+                       // rebind existing still valid notification
+                               if (edg_wll_NotifIdParse(notif->id_str, &notif->id)) {
+                                       lprintf_ctx(t, WRN, ctx, "can't parse %d. notification '%s' (%s)", i, notif->id_str, rtm_notiftype2str(notif->type));
+                                       notif->valid = 0;
+                                       notif->id = NULL;
+                                       i--;
+                                       continue;
+                               }
+                               notif->valid = now + config.ttl;
+                               if (edg_wll_NotifBind(ctx, notif->id, sock, config.local_address, &notif->valid)) {
+                                       lprintf_ctx(t, WRN, ctx, "can't rebind %d. notification '%s' (%s)", i, notif->id_str, rtm_notiftype2str(notif->type));
+                                       notif->valid = 0;
+                                       edg_wll_NotifIdFree(notif->id);
+                                       notif->id = NULL;
+                                       i--;
+                                       continue;
+                               }
+                               lprintf(t, INF, "bound %d. notification '%s' (%s), valid: %s", i, notif->id_str, rtm_notiftype2str(notif->type), time2str(t, notif->valid));
+                               rtm_update_error_state(t, notif, i, 0);
+                               // no bootstrap here, reliable delivery will send changes
+                               updated = 1;
+                       } else if (!notif->refresh || notif->refresh <= now) {
+                       // refresh notification
+                               time_t valid;
+
+                               valid = now + config.ttl;
+                               if (edg_wll_NotifRefresh(ctx, notif->id, &valid)) {
+                                       lprintf_ctx(t, WRN, ctx, "can't refresh %d. notification '%s' (%s), will try up to %s...", i, notif->id_str, rtm_notiftype2str(notif->type), time2str(t, notif->valid - RTM_NOTIF_TTL_TO_DEAD));
+                                       // refresh failed, just move the refresh time...
+                                       updated = 1;
+                               } else {
+                                       notif->valid = valid;
+                                       lprintf(t, INF, "refreshed %d. notification '%s' (%s), valid: %s", i, notif->id_str, rtm_notiftype2str(notif->type), time2str(t, notif->valid));
+                                       rtm_update_error_state(t, notif, i, 0);
+                                       updated = 1;
+                               }
+                       } else {
+                               lprintf(t, INS, "no change in %d. notification '%s' (%s)", i, notif->id_str, rtm_notiftype2str(notif->type));
+                       }
+
+cont:
+                       if (updated || error) {
+                               if (!error) {
+                                       assert(notif->valid);
+                                       notif->refresh = notif->valid ? (now + ((notif->valid - now) >> 1)) : 0;
+                               
+                               }
+                               // create or refresh OK, bootstrap if needed OK, store the new notification
+                               updated = 0;
+                               error = 0;
+
+                               // quicker refresh (or recreate) if needed
+                               now = time(NULL);
+                               if (notif->valid && now >= notif->refresh) {
+                                       lprintf(t, WRN, "operation not in time, refreshing/recreating the notification '%s' (%s) now", notif->id_str, rtm_notiftype2str(notif->type));
+                                       i--;
+                                       continue;
+                               }
+                               rtm_update_notif(t, notif, 1);
+                       }
+
+                       // compute time of the next event from the new refresh on notification
+                       if (t->next_refresh > notif->refresh) t->next_refresh = notif->refresh;
+               }
+
+               // receive
+               //
+               // cycle here locally around NotifReceive, we know about next
+               // refresh time
+               //
+               lprintf(t, DBG, "waiting for the notifications up to %s...", t->next_refresh ? time2str(t, t->next_refresh) : "0 (no wait)");
+               while (t->next_refresh > now && !quit) {
+                       to.tv_sec = t->next_refresh - now;
+                       if (to.tv_sec > RTM_NOTIF_READ_TIMEOUT) to.tv_sec = RTM_NOTIF_READ_TIMEOUT;
+                       to.tv_usec = 0;
+                       memset(&jobstat, 0, sizeof(jobstat));
+                       notifid = NULL;
+                       err = edg_wll_NotifReceive(ctx, sock, &to, &jobstat, &notifid);
+                       lprintf(t, INS, "received, err=%d%s", err, err == ETIMEDOUT ? " (timeout)":"");
+                       if (err != 0) {
+                               if (err != ETIMEDOUT) {
+                                       lprintf_ctx(t, ERR, ctx, "can't receive notifications");
+                                       // don't cycle too quick...
+                                       sleep(1);
+                               }
+                               // lazily refresh persistent storage here, only after timeouts
+                               if (received) {
+                                       lprintf(t, DBG, "storing notification times");
+                                       rtm_update_notif(t, NULL, 1);
+                                       received = 0;
+                               }
+                       } else {
+                               char *jobidstr, *notifidstr;
+                               double last_update;
+
+                               if (notifid) {
+                                       jobidstr = jobstat.jobId ? glite_jobid_unparse(jobstat.jobId) : NULL;
+                                       notifidstr = notifid ? edg_wll_NotifIdUnparse(notifid) : NULL;
+                                       for (i = 0; i < t->nservers && (!t->notifs[i].id_str || strcmp(notifidstr, t->notifs[i].id_str) != 0); i++);
+                                       if (i == t->nservers) {
+                                               lprintf(t, ERR, "received notify '%s' not found", notifidstr);
+                                       } else {
+                                               received = 1;
+                                               notif = t->notifs + i;
+                                               //
+                                               // last changed time from the arrived notification
+                                               //
+                                               last_update = jobstat.lastUpdateTime.tv_sec + jobstat.lastUpdateTime.tv_usec / 1000000.0;
+                                               if (last_update > notif->last_update) notif->last_update = last_update;
+                                               db_store_change(t, notif, i, &jobstat);
+                                               rtm_update_notif(t, notif, 0);
+                                       }
+                                       free(jobidstr);
+                                       free(notifidstr);
+                               }
+                       }
+                       if (jobstat.state != EDG_WLL_JOB_UNDEF) edg_wll_FreeStatus(&jobstat);
+                       if (notifid) edg_wll_NotifIdFree(notifid);
+
+                       now = time(NULL);
+               } // receive
+       } // main loop
+
+exit:  
+       if (sock != -1) close(sock);
+//     for (i = 0; conditions[i]; i++) free(conditions[i]);
+       if (t->nservers && quit != RTM_QUIT_PRESERVE && quit != RTM_QUIT_RELOAD) {
+               for (i = 0; i < t->nservers; i++) {
+                       if (t->notifs[i].id) {
+                               char *notifidstr;
+
+                               notifidstr = edg_wll_NotifIdUnparse(t->notifs[i].id);
+                               edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER, t->notifs[i].server);
+                               edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER_PORT, t->notifs[i].port);
+                               if (edg_wll_NotifDrop(ctx, t->notifs[i].id)) {
+                                       lprintf_ctx(t, WRN, ctx, "can't drop %s (%s)", notifidstr, rtm_notiftype2str(t->notifs[i].type));
+                               } else {
+                                       lprintf(t, INF, "notification %s (%s) dropped", notifidstr, rtm_notiftype2str(t->notifs[i].type));
+                               }
+                               rtm_drop_notif(t, t->notifs[i].id_str, 0);
+                               free(notifidstr);
+                       }
+               }
+               rtm_update_notif(t, NULL, 1);
+       }
+#ifdef WITH_LBU_DB
+       if (t->insertcmd) glite_lbu_FreeStmt(&t->insertcmd);
+       if (t->updatecmd) glite_lbu_FreeStmt(&t->updatecmd);
+       if (t->updatecmd_vo) glite_lbu_FreeStmt(&t->updatecmd_vo);
+       if (t->updatecmd_mon) glite_lbu_FreeStmt(&t->updatecmd_mon);
+       if (t->deletecmd) glite_lbu_FreeStmt(&t->deletecmd);
+       db_free(t, t->dbctx);
+#endif
+       if (ctx) edg_wll_FreeContext(ctx);
+       lprintf(t, DBG, "thread ended");
+       pthread_exit(NULL);
+       return NULL;
+}
+
+
+int reconcile_threads() {
+       int iserver, ithread, inotif, gran, mod, nnotifs;
+       int i, j, oldn, type, typestart, typeend;
+       notif_t *a, *b;
+       edg_wll_Context ctx = NULL;
+       edg_wll_NotifId notifid;
+       thread_t *t;
+
+       if (!config.cleanup) {
+               if (config.silly) {
+                       typestart = RTM_NOTIF_TYPE_OLD;
+                       typeend = RTM_NOTIF_TYPE_OLD;
+                       nnotifs = 1;
+               } else {
+                       typestart = RTM_NOTIF_TYPE_STATUS;
+                       typeend = RTM_NOTIF_TYPE_JDL;
+                       nnotifs = 2;
+               }
+
+               oldn = db.n;
+
+               // distribute LB servers between threads
+               // (always use existing loaded notification when found)
+               threads = (thread_t *)calloc(config.nthreads, sizeof(thread_t));
+               gran = config.nservers / config.nthreads, mod = config.nservers % config.nthreads;
+               t = NULL;
+               ithread = 0;
+               inotif = 0;
+               for (iserver = 0; iserver < config.nservers; iserver++) {
+                       // new thread
+                       if (!t || inotif + nnotifs > t->nservers) {
+                               assert(ithread < config.nthreads);   // proper number of threads
+                               assert(!t || inotif == t->nservers); // start or exactly distributed
+                               t = threads + ithread;
+                               t->nservers = nnotifs * ((ithread < mod) ? gran + 1 : gran);
+                               t->notifs = (notif_t *)calloc(t->nservers, sizeof(notif_t));
+                               lprintf(NULL, DBG, "%d. thread: %d notifications", ithread, t->nservers);
+                               ithread++;
+                               inotif = 0;
+                       }
+
+                       // next configured server
+                       a = config.notifs + iserver;
+                       for (type = typestart; type <= typeend; type++) {
+                               // find or create all notification types
+                               b = db_search_notif_by_server(db.notifs, oldn, a->server, a->port, type);
+                               if (!b) b = db_add_notif(NULL, type, 0, 0, 0, strdup(a->server), a->port, 1, 0);
+                               else {
+                                       if (b->id_str) {
+                                               lprintf(NULL, INF, "found previous notification '%s' (%s)", b->id_str, rtm_notiftype2str(b->type));
+                                       } else {
+                                               lprintf(NULL, INF, "found previous server %s:%d (%s), %d errors", b->server, b->port, rtm_notiftype2str(b->type), b->error);
+                                       }
+                                       b->active = 1;
+                               }
+                               // and add each to the thread
+                               notif_copy(t->notifs + inotif, b);
+                               lprintf(NULL, INS, "thread[%d][%d] <- %s:%d (%s), id %s", ithread-1, inotif, b->server, b->port, rtm_notiftype2str(b->type), b->id_str);
+                               inotif++;
+                       }
+               }
+               j = 0;
+               for (i = 0; i < db.n; i++)
+                       if (db.notifs[i].active) j++;
+               assert(j % nnotifs == 0); // each server all notifs
+       }
+
+       if (edg_wll_InitContext(&ctx) != 0) {
+               lprintf(NULL, ERR, "can't init LB context: %s", strerror(errno));
+               return 1;
+       }
+       if (config.cert) edg_wll_SetParam(ctx, EDG_WLL_PARAM_X509_CERT, config.cert);
+       if (config.key) edg_wll_SetParam(ctx, EDG_WLL_PARAM_X509_KEY, config.key);
+       for (j = 0; j < db.n; j++) {
+               if (!db.notifs[j].active) {
+                       if (db.notifs[j].id_str) {
+                               lprintf(NULL, INF, "dropping previous notification '%s' (%s)", db.notifs[j].id_str, rtm_notiftype2str(db.notifs[j].type));
+                               if (edg_wll_NotifIdParse(db.notifs[j].id_str, &notifid)) {
+                                       lprintf(NULL, WRN, "can't parse notification ID '%s'", db.notifs[j].id_str);
+                                       continue;
+                               }
+                               edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER, db.notifs[j].server);
+                               edg_wll_SetParam(ctx, EDG_WLL_PARAM_NOTIF_SERVER_PORT, db.notifs[j].port);
+                               if (edg_wll_NotifDrop(ctx, notifid) != 0) {
+                                       lprintf_ctx(NULL, WRN, ctx, "can't drop %s (%s)", db.notifs[j].id_str, rtm_notiftype2str(db.notifs[j].type));
+                               }
+                               edg_wll_NotifIdFree(notifid);
+                               notif_invalidate(db.notifs + j);
+                       }
+               }
+       }
+       edg_wll_FreeContext(ctx);
+
+       return db_save_notifs(NULL);
+}
+
+
+void usage(const char *prog) {
+       fprintf(stderr, "Usage: %s [options]\n"
+               "       -h, --help         display this help\n"
+               "       -v, --version      display version\n"
+               "       -d, --debug LEVEL  debug level (0=error,1=warn,2=info,3=debug,4=insane,\n"
+               "                          +8=not fork)\n"
+               "       -D, --daemonize    daemonize\n"
+               "       -i, --pidfile      the file with process ID\n"
+               "       -s, --threads N    number of slave threads\n"
+               "       -t, --ttl TIME     time to live (validity) of the notifications\n"
+               "                          in seconds (%d)\n"
+               "       -H, --history      historic dive in seconds (<=0 is unlimited)\n"
+               "       -c, --config       config file name (list of LB servers), precedence before " RTM_DB_TABLE_LBS " table\n"
+#ifdef WITH_LBU_DB
+               "       -m, --pg           db connection string (user/pwd@server:dbname) to " RTM_DB_TABLE_LBS " table\n"
+#endif
+               "       -n, --notifs       file for persistent information about active\n"
+               "                          notifications\n"
+               "       -p, --port         listen only on this port (default: use any)\n"
+               "       -C, --cert         X509 certificate file\n"
+               "       -K, --key          X509 key file\n"
+               "       -o, --old          \"silly\" mode for old L&B 1.9 servers\n"
+               "       -l, --cleanup      clean up the notifications and exit\n"
+               "       -u, --no-purge     disable purging from RTM database\n"
+               "       -w, --wlcg         enable messaging for dashboard\n"
+               "       --wlcg-binary      full path to msg-publish binary\n"
+               "       --wlcg-topic       topic for msg-publish\n"
+               "       --wlcg-config      config file for msg-publish\n"
+               "       --wlcg-flush       send message on each notification\n"
+               , prog, RTM_NOTIF_TTL);
+       fprintf(stderr, "\n");
+       fprintf(stderr, "List of L&B servers: first it's read the config file if specified (-c option). When config file is not used and connection to database is specified, it's tried DB table " RTM_DB_TABLE_LBS ".\n");
+       fprintf(stderr, "\n");
+}
+
+
+int config_preload(int argn, char *argv[]) {
+       int opt, intval, index;
+       char *err, *s;
+
+       while ((opt = getopt_long(argn, argv, opts_line, opts, &index)) != EOF) {
+               switch (opt) {
+               case 'h':
+               case '?':
+                       usage(argv[0]);
+                       return 1;
+               case 'v':
+                       fprintf(stderr, "%s: %s\n", argv[0], rcsid);
+                       return 1;
+               case 'd':
+                       intval = strtol(optarg, &err, 10);
+                       if (err && err[0]) {
+                               lprintf(NULL, ERR, "debug level number required");
+                               return 2;
+                       }
+                       config.debug = (intval & DEBUG_LEVEL_MASK);
+                       config.guard = !(intval & DEBUG_GUARD_MASK);
+                       break;
+               case 'D':
+                       config.daemonize = 1;
+                       break;
+               case 'i':
+                       config.pidfile = strdup(optarg);
+                       break;
+               case 's':
+                       intval = strtol(optarg, &err, 10);
+                       if (err && err[0]) {
+                               lprintf(NULL, ERR, "number of threads required");
+                               return 2;
+                       }
+                       config.nthreads = intval;
+                       break;
+               case 't':
+                       intval = strtol(optarg, &err, 10);
+                       if (err && err[0]) {
+                               lprintf(NULL, ERR, "requested validity in seconds required");
+                               return 2;
+                       }
+                       config.ttl = intval;
+                       break;
+               case 'H':
+                       intval = strtol(optarg, &err, 10);
+                       if (err && err[0]) {
+                               lprintf(NULL, ERR, "historic dive in seconds required");
+                               return 2;
+                       }
+                       config.dive = intval;
+                       break;
+               case 'c':
+                       free(config.config_file);
+                       config.config_file = strdup(optarg);
+                       break;
+               case 'n':
+                       free(config.notif_file);
+                       config.notif_file = strdup(optarg);
+                       break;
+               case 'p':
+                       listen_port = atoi(optarg);
+                       break;
+               case 'm':
+                       free(config.dbcs);
+                       config.dbcs = strdup(optarg);
+                       break;
+               case 'C':
+                       free(config.cert);
+                       config.cert = strdup(optarg);
+                       break;
+               case 'K':
+                       free(config.key);
+                       config.key = strdup(optarg);
+                       break;
+               case 'l':
+                       config.cleanup = 1;
+                       break;
+               case 'w':
+                       config.wlcg = 1;
+                       break;
+               case 'o':
+                       config.silly = 1;
+                       break;
+               case 'u':
+                       config.no_purge = 1;
+                       break;
+               case 0:
+                       switch(index) {
+                       case 0:
+                               config.wlcg_binary = strdup(optarg);
+                               break;
+                       case 1:
+                               config.wlcg_config = strdup(optarg);
+                               break;
+                       case 2:
+                               config.wlcg_topic = strdup(optarg);
+                               break;
+                       case 3:
+                               config.wlcg_flush = 1;
+                               break;
+                       default:
+                               lprintf(NULL, ERR, "crazy option, index %d", index);
+                               break;
+                       }
+                       break;
+               }
+       }
+       if (!config.notif_file) config.notif_file = strdup(RTM_FILE_NOTIFS);
+       if (config.wlcg) {
+               if (!config.wlcg_binary) config.wlcg_binary = strdup(WLCG_BINARY);
+               if (!config.wlcg_config) config.wlcg_config = strdup(WLCG_CONFIG);
+               if (!config.wlcg_topic) config.wlcg_topic = strdup(WLCG_TOPIC);
+       }
+#ifdef WITH_OLD_LB
+       if (!config.silly) {
+               lprintf(NULL, WRN, "compiled with older LB library, switching on silly mode");
+               config.silly = 1;
+       }
+#endif
+
+       if ((s = getenv("GLITE_LB_HARVESTER_NO_REMOVE")) != NULL) {
+               if (s[0] != '0' && strcasecmp(s, "false") != 0) config.wlcg_no_remove = 1;
+       }
+
+       if (INF <= config.debug) {
+               lprintf(NULL, INF, "threads: %d", config.nthreads);
+               lprintf(NULL, INF, "notifs ttl: %d", config.ttl);
+               lprintf(NULL, INF, "historic dive: %d", config.dive);
+               if (config.dbcs) {
+                       lprintf(NULL, INF, "database storage: '%s'", config.dbcs);
+               } else {
+                       lprintf(NULL, INF, "file storage: '%s'", config.notif_file);
+               }
+               lprintf(NULL, INF, "WLCG messaging: %s%s", config.wlcg ? "enabled" : "disabled", config.wlcg_no_remove ? " (not removing tmp files)" : "");
+               lprintf(NULL, INF, "debug level: %d", config.debug);
+               lprintf(NULL, INF, "daemonize: %s", config.daemonize ? "enabled" : "disabled");
+               lprintf(NULL, INF, "fork guard: %s", config.guard ? "enabled" : "disabled");
+               lprintf(NULL, INF, "silly compatibility mode: %s", config.silly ? "enabled" : "disabled");
+               lprintf(NULL, INF, "purge: %s", !config.no_purge ? "enabled" : "disabled");
+       }
+
+       return 0;
+}
+
+
+int config_load() {
+       char line[LINE_MAX], *port, *s;
+       FILE *f;
+       void *tmp;
+       int i, n;
+#ifdef WITH_LBU_DB
+       int major, minor, sub, version;
+       char *results[2];
+       char *result = NULL;
+       glite_lbu_Statement stmt = NULL;
+       int err = 0;
+#endif
+
+       if (config.config_file) {
+               if ((f = fopen(config.config_file, "rt")) == NULL) {
+                       lprintf(NULL, ERR, "can't open config file '%s': %s", config.config_file, strerror(errno));
+                       return 1;
+               }
+
+               n = 10;
+               while (fgets(line, sizeof(line), f) != NULL) {
+                       if ((s = strpbrk(line, "\n\r")) != NULL) s[0] = '\0';
+                       if (line[0] == '\0' || line[0] == '#') continue;
+                       if (config.nservers >= n || !config.notifs) {
+                               n = 2 * n;
+                               if ((tmp = (notif_t *)realloc(config.notifs, n * sizeof(notif_t))) == NULL) {
+                                       lprintf(NULL, ERR, "insufficient memory");
+                                       return 1;
+                               }
+                               config.notifs = tmp;
+                               memset(config.notifs + config.nservers, 0, (n - config.nservers) * sizeof(notif_t));
+                       }
+                       if ((port = strrchr(line, ':')) != NULL) { port[0] = '\0'; port++; }
+                       config.notifs[config.nservers].server = strdup(line);
+                       config.notifs[config.nservers++].port = (port && port[0]) ? atoi(port) : GLITE_JOBID_DEFAULT_PORT;
+               }
+
+               fclose(f);
+       } else
+#ifdef WITH_LBU_DB
+       if (db.dbctx) {
+               if ((err = glite_lbu_ExecSQL(db.dbctx, "SELECT COUNT(*) FROM " RTM_DB_TABLE_LBS, &stmt)) < 0 ||
+                   (err = glite_lbu_FetchRow(stmt, 1, NULL, &result)) < 0) {
+                       goto err;
+               }
+               if (err == 0) {
+                       lprintf(NULL, ERR, "can't count LB servers");
+                       goto err;
+               }
+               n = atoi(result);
+               free(result);
+               glite_lbu_FreeStmt(&stmt);
+
+               config.notifs = calloc(n, sizeof(notif_t));
+               config.nservers = 0;
+               if ((err = glite_lbu_ExecSQL(db.dbctx, "SELECT DISTINCT ip, serv_version FROM " RTM_DB_TABLE_LBS, &stmt)) < 0) {
+                       goto err;
+               }
+               while (config.nservers < n && (err = glite_lbu_FetchRow(stmt, 2, NULL, results)) > 0) {
+                       if (sscanf(results[1], "%d.%d.%d", &major, &minor, &sub) != 3) {
+                               lprintf(NULL, ERR, "can't parse LB server version '%s'", results[1]);
+                               free(results[1]);
+                               break;
+                       }
+                       version = 10000 * major + 100 * minor + sub;
+                       if (version >= 20000 || config.silly) {
+                               config.notifs[config.nservers].server = strdup(results[0]);
+                               config.notifs[config.nservers++].port = GLITE_JOBID_DEFAULT_PORT;
+                       } else {
+                               lprintf(NULL, INF, "skipped older LB server %s (version '%s')", results[0], results[1]);
+                       }
+                       free(results[0]);
+                       free(results[1]);
+               }
+               if (err < 0) goto err;
+               glite_lbu_FreeStmt(&stmt);
+       }
+#endif
+
+       if (INF <= config.debug) {
+               lprintf(NULL, INF, "servers: %d", config.nservers);
+               for (i = 0; i < config.nservers; i++) lprintf(NULL, INF, "  %s:%d", config.notifs[i].server, config.notifs[i].port);
+       }
+
+       return 0;
+#ifdef WITH_LBU_DB
+err:
+       if (err) lprintf_dbctx(NULL, ERR, "can't get LB servers");
+       if (stmt) glite_lbu_FreeStmt(&stmt);
+       if (result) free(result);
+#endif
+       return 1;
+}
+
+
+void config_free() {
+       int i;
+
+       for (i = 0; i < config.nservers; i++) free(config.notifs[i].server);
+       free(config.config_file);
+       free(config.notif_file);
+       free(config.pidfile);
+       free(config.dbcs);
+       free(config.notifs);
+       free(config.cert);
+       free(config.key);
+       free(config.wlcg_binary);
+       free(config.wlcg_config);
+       free(config.wlcg_topic);
+}
+
+
+// on keyboard cleanup notification, on termination signal break with
+// notification preserved
+void handle_signal(int num) {
+       lprintf(NULL, INF, "received signal %d", num);
+       switch (num) {
+       case SIGINT:
+       case SIGTERM:
+       default:
+               quit = RTM_QUIT_PRESERVE;
+               break;
+       }
+}
+
+
+int main(int argn, char *argv[]) {
+       struct sigaction sa;
+       sigset_t sset;
+       int i, j;
+       double t1, t2, last_summary = 0, start_time;
+       thread_t *t;
+       struct stat pstat;
+       pid_t watched;
+       int status;
+       edg_wll_Context ctx = NULL;
+       int retval = RTM_EXIT_ERROR;
+       int cert_mtime = 0;
+
+       // load basic configurations
+       switch (config_preload(argn, argv)) {
+       case 0:
+               break;
+       case 1:
+               retval = RTM_EXIT_OK;
+               goto quit_guard0;
+               break;
+       default:
+               retval = RTM_EXIT_ERROR;
+               goto quit_guard0;
+       }
+
+       // daemonize
+       if (config.pidfile) {
+               FILE *f;
+               char s[256];
+
+               if ((f = fopen(config.pidfile, "rt"))) {
+                       if (fscanf(f, "%255[^\n\r]", s) == 1) {
+                               if (kill(atoi(s),0)) {
+                                       lprintf(NULL, WRN, "stale pidfile, pid = %s, pidfile '%s'", s, config.pidfile);
+                                       fclose(f);
+                               }
+                               else {
+                                       lprintf(NULL, ERR, "another instance running, pid = %s, pidfile '%s'", s, config.pidfile);
+                                       fclose(f);
+                                       goto quit_guard0;
+                               }
+                       } else {
+                               lprintf(NULL, ERR, "another instance possibly running, can't read pidfile '%s': %s", config.pidfile, strerror(errno));
+                               fclose(f);
+                               goto quit_guard0;
+                       }
+               } else if (errno != ENOENT) {
+                       lprintf(NULL, ERR, "error opening pidfile '%s': %s", config.pidfile, strerror(errno));
+                       goto quit_guard0;
+               }
+       }
+       if (config.daemonize) {
+               if (daemon(0, 0) == -1) {
+                       lprintf(NULL, ERR, "can't daemonize: %s", strerror(errno));
+                       goto quit_guard0;
+               }
+       }
+
+       // disable signals to the guardian
+       sigemptyset(&sset);
+       sigaddset(&sset, SIGABRT);
+       sigaddset(&sset, SIGTERM);
+       sigaddset(&sset, SIGINT);
+       pthread_sigmask(SIG_BLOCK, &sset, NULL);
+
+       if (!config.guard) {
+       // not guard
+               if (config.pidfile) {
+                       FILE *f;
+
+                       if ((f = fopen(config.pidfile, "wt")) == NULL) {
+                               lprintf(NULL, ERR, "can't create pidfile '%s': %s", config.pidfile, strerror(errno));
+                               goto quit_guard0;
+                       }
+                       fprintf(f, "%d", getpid());
+                       fclose(f);
+               }
+       } else
+       // guard
+       while ((watched = fork()) != 0) {
+               if (watched == -1) {
+                       lprintf(NULL, ERR, "fork() failed: %s", strerror(errno));
+                       goto quit_guard;
+               }
+               if (config.pidfile) {
+                       FILE *f;
+
+                       if ((f = fopen(config.pidfile, "wt")) == NULL) {
+                               lprintf(NULL, ERR, "can't create pidfile '%s': %s", config.pidfile, strerror(errno));
+                               goto quit_guard0;
+                       }
+                       fprintf(f, "%d", watched);
+                       fclose(f);
+               }
+               if (waitpid(watched, &status, 0) == -1) {
+                       lprintf(NULL, ERR, "waitpid() failed: %s", strerror(errno));
+                       // orpaned child will restart later anyway,
+                       // better to end the child process just now
+                       kill(watched, SIGTERM);
+                       goto quit_guard;
+               }
+               if (WIFSIGNALED(status)) {
+                       switch (WTERMSIG(status)) {
+                       case SIGSEGV:
+                       case SIGILL:
+                       case SIGABRT:
+#ifdef SIGBUS
+                       case SIGBUS:
+#endif
+                               lprintf(NULL, ERR, "caught signal %d from process %d, resurrecting...", WTERMSIG(status), watched);
+                               // slow down the core generator ;-)
+                               // disabled signals and ended child in pidfile, live with it
+                               pthread_sigmask(SIG_UNBLOCK, &sset, NULL);
+                               if (config.pidfile) {
+                                       if (remove(config.pidfile) == -1) lprintf(NULL, WRN, "can't remove pidfile '%s': %s", config.pidfile, strerror(errno));
+                               }
+                               sleep(2);
+                               pthread_sigmask(SIG_BLOCK, &sset, NULL);
+                               break;
+                       default:
+                               lprintf(NULL, WRN, "ended with signal %d", WTERMSIG(status));
+                               goto quit_guard;
+                       }
+               } else if (WIFEXITED(status)) {
+                       retval = WEXITSTATUS(status);
+                       switch(retval) {
+                       case RTM_EXIT_OK:
+                               lprintf(NULL, INF, "exit with status %d, OK", retval);
+                               goto quit_guard;
+                       case RTM_EXIT_RELOAD:
+                               lprintf(NULL, INF, "exit with status %d, reloading", retval);
+                               break;
+                       default:
+                               lprintf(NULL, WRN, "exit with status %d, error", retval);
+                               goto quit_guard;
+                       }
+               } else {
+                       lprintf(NULL, ERR, "unknown child status");
+                       goto quit_guard;
+               }
+       }
+
+       // child continues...
+
+       // threads && Globus
+       if (edg_wll_gss_initialize()) {
+               lprintf(NULL, ERR, "can't initialize GSS");
+               goto quit_guard;
+       }
+
+#ifndef WITH_OLD_LB
+       // connection pool manually (just for tuning memory leaks)
+       if (!edg_wll_initConnections()) {
+               lprintf(NULL, ERR, "can't initialize LB connections");
+               goto quit_guard;
+       }
+#endif
+
+#ifdef WITH_LBU_DB
+       // database
+       switch(db_init(NULL, &db.dbctx)) {
+       case 0:
+               break;
+       case -1:
+               // no db
+               break;
+       default:
+               // error
+               goto quit;
+       }
+#endif
+
+       // load configurations
+       if (config_load()) goto quit;
+#ifdef WITH_OLD_LB
+       // other client certificate settings ignored by older globus,
+       // using environment (certificate the same for all threads)
+       {
+               char *s;
+
+               if (config.cert) {
+                       asprintf(&s, "X509_USER_CERT=%s", config.cert);
+                       putenv(s);
+               }
+               if (config.key) {
+                       asprintf(&s, "X509_USER_KEY=%s", config.key);
+                       putenv(s);
+               }
+       }
+#endif
+
+       // load previous notifications
+       if (load_notifs()) goto quit;
+       // compare lb servers from configuration and notifications,
+       // or clean up and exit if specified
+       if (reconcile_threads()) goto quit;
+       if (config.cleanup) {
+               retval = RTM_EXIT_OK;
+               goto quit;
+       }
+
+       // signal handler
+       sa.sa_handler = handle_signal;
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = SA_RESETHAND;
+       if (sigaction(SIGABRT, &sa, NULL) == -1
+           || sigaction(SIGTERM, &sa, NULL) == -1
+           || sigaction(SIGINT, &sa, NULL) == -1) {
+               lprintf(NULL, ERR, "can't handle signal: %s", strerror(errno));
+               goto quit;
+       }
+       // enable signals in main
+       pthread_sigmask(SIG_UNBLOCK, &sset, NULL);
+
+       // launch the threads
+       for (i = 0; i < config.nthreads; i++) {
+               t = threads + i;
+               t->id = i;
+               if (pthread_create(&threads[i].thread, NULL, notify_thread, t) != 0) {
+                       lprintf(NULL, ERR, "[main] can't create %d. thread: %s\n", i, strerror(errno));
+                       goto quit;
+               }
+       }
+
+       edg_wll_InitContext(&ctx);
+       if (config.cert) edg_wll_SetParam(ctx, EDG_WLL_PARAM_X509_CERT, config.cert);
+       if (config.key) edg_wll_SetParam(ctx, EDG_WLL_PARAM_X509_KEY, config.key);
+       last_summary = 0;
+       start_time = rtm_gettimeofday();
+       while (!quit) {
+               t1 = rtm_gettimeofday();
+               if (t1 - last_summary > RTM_SUMMARY_POLL_TIME) {
+                       last_summary = t1;
+                       rtm_summary(ctx, &db);
+               }
+               if (config.guard) {
+                       if (t1 - start_time > RTM_SUICIDE_TIME) {
+                               quit = RTM_QUIT_RELOAD;
+                               lprintf(NULL, INF, "preventive suicide");
+                               break;
+                       }
+                       if (config.cert) {
+                               if (stat(config.cert, &pstat) == 0) {
+                                       if (!cert_mtime) cert_mtime = pstat.st_mtime;
+                                       if (cert_mtime < pstat.st_mtime) {
+                                               lprintf(NULL, INF, "certificate '%s' changed, reloading", config.cert);
+                                               quit = RTM_QUIT_RELOAD;
+                                               break;
+                                       }
+                               } else {
+                                       lprintf(NULL, ERR, "can't check certificate file '%s'", config.cert, strerror(errno));
+                               }
+                       }
+               }
+               t2 = rtm_gettimeofday();
+               if (t2 - t1 < RTM_IDLE_POLL_TIME) usleep((RTM_IDLE_POLL_TIME + t1 - t2) * 1000000);
+       }
+       retval = quit == RTM_QUIT_RELOAD ? RTM_EXIT_RELOAD : RTM_EXIT_OK;
+quit:
+       // cleanup on error
+       if (!quit) quit = RTM_QUIT_CLEANUP;
+       if (threads) {
+               for (i = 0; i < config.nthreads; i++) {
+                       t = threads + i;
+                       if (t->thread) pthread_join(t->thread, NULL);
+                       for (j = 0; j < t->nservers; j++) notif_free(t->notifs + j);
+                       free(t->notifs);
+               }
+               free(threads);
+       }
+
+       if (config.pidfile && !config.guard) {
+               if (remove(config.pidfile) == -1) lprintf(NULL, WRN, "can't remove pidfile '%s': %s", config.pidfile, strerror(errno));
+       }
+
+#ifdef WITH_LBU_DB
+       db_free(NULL, db.dbctx);
+#endif
+       edg_wll_FreeContext(ctx);
+       db_free_notifs();
+       config_free();
+#ifndef WITH_OLD_LB
+       edg_wll_poolFree();
+#endif
+
+       return retval;
+
+quit_guard:
+       if (config.pidfile) {
+               if (remove(config.pidfile) == -1) lprintf(NULL, WRN, "can't remove pidfile '%s': %s", config.pidfile, strerror(errno));
+       }
+quit_guard0:
+       config_free();
+       return retval;
+}
index a759033..b6f57a9 100644 (file)
@@ -1,15 +1,12 @@
 # defaults
-top_srcdir=.
+top_srcdir=..
 stagedir=.
-distdir=.
 globalprefix=glite
 lbprefix=lb
 package=glite-lb-client
 version=0.0.0
 PREFIX=/opt/glite
 
-glite_location=/opt/glite
-globus_prefix=/opt/globus
 nothrflavour=gcc32
 thrflavour=gcc32pthr
 
@@ -26,10 +23,6 @@ VERSION=-DVERSION=\"GLite-${version}\"
 
 SUFFIXES=.no
 
-GLOBUSINC=-I${globus_prefix}/include/${nothrflavour}
-
-GLOBUSTHRINC=-I${globus_prefix}/include/${thrflavour}
-
 ifdef LB_STANDALONE
        LB_STANDALONE_FLAGS:=-DLB_STANDALONE
 endif
@@ -45,28 +38,23 @@ else
        LOGD:=glite-lb-logd
        INTERLOGD:=glite-lb-interlogd
        NOTIF_INTERLOGD:=glite-lb-notif-interlogd
+       WS_INTERLOGD:=glite-lb-ws-interlogd
 endif
 
 DEBUG:=-g -O0
 CFLAGS:=${DEBUG} \
-       -I${stagedir}/include -I${top_srcdir}/src \
+       -I${stagedir}/include -I${top_srcdir}/src -I${top_srcdir}/interface \
        -D_GNU_SOURCE \
        ${COVERAGE_FLAGS} \
        ${VERSION} ${LB_STANDALONE_FLAGS} ${LB_PERF_FLAGS} 
 
-LDFLAGS:=-L${stagedir}/lib \
+LDFLAGS:=-L${stagedir}/${libdir} \
        ${COVERAGE_FLAGS} 
-LINK:=libtool --mode=link ${CC} ${LDFLAGS}
-LINKXX:=libtool --mode=link ${CXX} -rpath ${stagedir}/lib ${LDFLAGS} 
-INSTALL:=libtool --mode=install install
-
-GLOBUS_LIBS:= -L${globus_prefix}/lib \
-       -lglobus_common_${nothrflavour} \
-       -lglobus_gssapi_gsi_${nothrflavour}
 
-GLOBUS_THRLIBS:= -L${globus_prefix}/lib \
-       -lglobus_common_${thrflavour} \
-       -lglobus_gssapi_gsi_${thrflavour}
+COMPILE:=libtool --mode=compile ${CC} 
+LINK:=libtool --mode=link ${CC} -rpath ${stagedir}/${libdir} ${LDFLAGS}
+LINKXX:=libtool --mode=link ${CXX} -rpath ${stagedir}/${libdir} ${LDFLAGS} 
+INSTALL:=libtool --mode=install install
 
 #ifneq (${expat_prefix},/usr)
 #      EXPAT_LIBS:=-L${expat_prefix}/lib
@@ -75,11 +63,15 @@ GLOBUS_THRLIBS:= -L${globus_prefix}/lib \
 #
 #EXT_LIBS:= ${EXPAT_LIBS}
 
+HDRS:=interlogd.h il_error.h
+
 COMMON_LIB:=-lglite_lb_common
 
+EXT_LIB:=-lglite_lbu_trio -lglite_lbu_log
+
 GLITE_GSS_LIB:=-lglite_security_gss
 
-TEST_LIBS:=-L${cppunit_prefix}/lib -lcppunit
+TEST_LIBS:=-L${cppunit_prefix}/${libdir} -lcppunit
 TEST_INC:=-I${cppunit_prefix}/include
 
 LOGD_OBJS:= logd_proto.o logd.o
@@ -87,11 +79,17 @@ LOGD_OBJS:= logd_proto.o logd.o
 LOGD_NOBJS:=${LOGD_OBJS:.o=.no}
 
 INTERLOG_OBJS:=il_error.o input_queue_socket.o \
-       recover.o send_event.o \
+       recover.o send_event.o plugin_mgr.o \
        event_queue.o event_store.o il_master.o interlogd.o \
-       queue_mgr.o server_msg.o queue_thread.o
+       queue_mgr.o server_msg.o queue_thread.o \
 
-INTERLOG_NOBJS:=${INTERLOG_OBJS:.o=.no}
+WS_INTERLOG_OBJS:=il_error.o input_queue_socket_http.o \
+       recover.o http.o send_event_http.o plugin_mgr.o \
+       event_queue.o event_store_http.o il_master.o interlogd.o \
+       queue_mgr_http.o server_msg_http.o queue_thread.o
+
+INTERLOG_NOTIF_OBJS:=${INTERLOG_OBJS:.o=.notif.lo}
+INTERLOG_WS_OBJS:=${WS_INTERLOG_OBJS:.o=.ws.o}
 INTERLOG_PERF_OBJS:=${INTERLOG_OBJS:.o=.perf.o}
 INTERLOG_EMPTY_OBJS:=${INTERLOG_OBJS:.o=.empty.o}
 #INTERLOG_INLINE_EMPTY_OBJS:=${INTERLOG_OBJS:.o=.io}
@@ -108,6 +106,7 @@ INTERLOG_TEST_OBJS:= \
        input_queue_socket.o \
        input_queue_socketTest.o \
        send_event.o \
+       plugin_mgr. o \
        event_queue.o \
        event_queueTest.o \
        IlTestBase.o \
@@ -118,51 +117,55 @@ MAN = $(MAN_GZ:.gz=)
 
 default: all 
 
-all compile: $(LOGD) $(INTERLOGD) ${MAN_GZ}
+all compile: $(LOGD) $(INTERLOGD) $(NOTIF_INTERLOGD) ${MAN_GZ}
 
 glite-lb-logd: ${LOGD_OBJS}
-       ${LINK} -o $@ ${LOGD_OBJS} ${COMMON_LIB}_${nothrflavour} 
+       ${LINK} -o $@ ${LOGD_OBJS} ${COMMON_LIB}_${nothrflavour} ${EXT_LIB}
 
 glite-lb-logd-perf: ${LOGD_OBJS}
-       ${LINK} -o $@ ${LOGD_OBJS} ${COMMON_LIB}_${nothrflavour} 
+       ${LINK} -o $@ ${LOGD_OBJS} ${COMMON_LIB}_${nothrflavour} ${EXT_LIB}
 
 glite-lb-logd-nofile: ${LOGD_NOBJS}
-       ${LINK} -o $@ ${LOGD_NOBJS} ${COMMON_LIB}_${nothrflavour} 
+       ${LINK} -o $@ ${LOGD_NOBJS} ${COMMON_LIB}_${nothrflavour} ${EXT_LIB}
 
 glite-lb-logd-perf-nofile: ${LOGD_NOBJS}
-       ${LINK} -o $@ ${LOGD_NOBJS} ${COMMON_LIB}_${nothrflavour} 
+       ${LINK} -o $@ ${LOGD_NOBJS} ${COMMON_LIB}_${nothrflavour} ${EXT_LIB}
 
 glite-lb-interlogd: ${INTERLOG_OBJS}
-       ${LINK} -o $@ ${INTERLOG_OBJS} ${COMMON_LIB}_${thrflavour} 
+       ${LINK} -o $@ ${INTERLOG_OBJS} ${COMMON_LIB}_${thrflavour} ${EXT_LIB}
 
-glite-lb-notif-interlogd: ${INTERLOG_NOBJS}
-       ${LINK} -o $@ ${INTERLOG_NOBJS} ${COMMON_LIB}_${thrflavour}
+glite-lb-notif-interlogd: ${INTERLOG_NOTIF_OBJS}
+       ${LINK} -export-dynamic -o $@ ${INTERLOG_NOTIF_OBJS} ${COMMON_LIB}_${thrflavour} ${EXT_LIB}
 
 glite-lb-interlogd-perf: ${INTERLOG_PERF_OBJS}
-       ${LINK} -o $@ ${INTERLOG_PERF_OBJS} ${COMMON_LIB}_${thrflavour}
+       ${LINK} -o $@ ${INTERLOG_PERF_OBJS} ${COMMON_LIB}_${thrflavour} ${EXT_LIB}
 
 glite-lb-interlogd-perf-empty: ${INTERLOG_EMPTY_OBJS}
-       ${LINK} -o $@ ${INTERLOG_EMPTY_OBJS} ${COMMON_LIB}_${thrflavour}
+       ${LINK} -o $@ ${INTERLOG_EMPTY_OBJS} ${COMMON_LIB}_${thrflavour} ${EXT_LIB}
+
+glite-lb-ws-interlogd: ${INTERLOG_WS_OBJS}
+       ${LINK} -o $@ ${INTERLOG_WS_OBJS} ${COMMON_LIB}_${thrflavour} ${EXT_LIB}
 
 #glite-lb-interlogd-perf-inline-empty: ${INTERLOG_INLINE_EMPTY_OBJS}
 #      ${LINK} -o $@ ${INTERLOG_INLINE_EMPTY_OBJS} \
 #              ${COMMON_LIB}_${thrflavour}
 
 ${MAN_GZ}: ${MAN}
+       rm -f ${MAN_GZ} ${MAN}
        cp $? .
-       gzip $(notdir $?)
+       gzip -f $(notdir $?)
 
 man: ${MAN_GZ}
 
 stage: compile
-       $(MAKE) install PREFIX=${stagedir} DOSTAGE=yes
+       $(MAKE) install PREFIX=${stagedir} 
 
 check: 
 # do nothing until test/ is really added to CVS
 # check.ll check.il
 
 #check.ll: logd_proto_test.o ll_test.o
-#      ${LINKXX} -o $@ ${COMMON_LIB}_${nothrflavour} ${EXT_LIBS} ${GLOBUS_LIBS} ${TEST_LIBS} $+
+#      ${LINKXX} -o $@ ${COMMON_LIB}_${nothrflavour} ${EXT_LIBS} ${TEST_LIBS} $+
 #      ./check.ll
 
 check.ll:
@@ -171,66 +174,59 @@ check.ll:
 check.il: ${INTERLOG_TEST_OBJS}
        ${LINKXX} -o $@ ${COMMON_LIB}_${thrflavour} ${GLITE_GSS_LIB}_${nothrflavour} ${TEST_LIBS} -lpthread $+
 
-dist: distsrc distbin
-
-distsrc:
-       mkdir -p ${top_srcdir}/${package}-${version}
-       cd ${top_srcdir} && GLOBIGNORE="${package}-${version}" && cp -Rf * ${package}-${version}
-       cd ${top_srcdir} && tar -czf ${distdir}/${package}-${version}_src.tar.gz --exclude-from=project/tar_exclude ${package}-${version}
-       rm -rf ${top_srcdir}/${package}-${version}
-
-distbin:
-       $(MAKE) install PREFIX=`pwd`/tmpbuilddir${stagedir}
-       save_dir=`pwd`; cd tmpbuilddir${stagedir} && tar -czf $$save_dir/${top_srcdir}/${distdir}/${package}-${version}_bin.tar.gz *; cd $$save_dir
-       rm -rf tmpbuilddir
-
 install:
        -mkdir -p ${PREFIX}/bin
        -mkdir -p ${PREFIX}/etc/init.d
        -mkdir -p ${PREFIX}/share/doc/${package}-${version}
        -mkdir -p ${PREFIX}/share/man/man8
+       -mkdir -p ${PREFIX}/include/glite/lb
        ${INSTALL} -m 755 ${LOGD} ${PREFIX}/bin
        ${INSTALL} -m 755 ${INTERLOGD} ${PREFIX}/bin
-#      if [ x${DOSTAGE} = xyes ]; then \
-#              ${INSTALL} -m 755 ${NOTIF_INTERLOGD} ${PREFIX}/bin; \
-#      fi
+       ${INSTALL} -m 755 ${NOTIF_INTERLOGD} ${PREFIX}/bin
 ifdef LB_PERF
+       -mkdir -p ${PREFIX}/sbin
        ${INSTALL} -m 755 ${top_srcdir}/src/perftest_ll.sh ${PREFIX}/sbin
        ${INSTALL} -m 755 ${top_srcdir}/src/perftest_il.sh ${PREFIX}/sbin
 endif
        ${INSTALL} -m 755 ${top_srcdir}/config/startup ${PREFIX}/etc/init.d/glite-lb-locallogger
        ${INSTALL} -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${version}
+       ( cd ${top_srcdir}/interface && ${INSTALL} -m 644 ${HDRS} ${PREFIX}/include/${globalprefix}/${lbprefix} )
+       ( cd ${top_srcdir}/project && ${INSTALL} -m 644 ChangeLog package.description package.summary ${PREFIX}/share/doc/${package}-${version} )
        ${INSTALL} -m 644 ${MAN_GZ} ${PREFIX}/share/man/man8
 
-${INTERLOG_NOBJS}: %.no: %.c
-       ${CC} ${CFLAGS} ${GLOBUSTHRINC} -DIL_NOTIFICATIONS -c $< -o $@
+${INTERLOG_NOTIF_OBJS}: %.notif.lo: %.c
+       ${COMPILE} ${CFLAGS} -DIL_NOTIFICATIONS -c $< -o $@
 
 ${INTERLOG_OBJS}: %.o: %.c
-       ${CC} ${CFLAGS} ${GLOBUSTHRINC} -c $< -o $@
+       ${COMPILE} ${CFLAGS} -c $< -o $@
+
+${INTERLOG_WS_OBJS}: %.ws.o: %.c
+       ${CC} ${CFLAGS} -DIL_WS -c $< -o $@
 
 ${INTERLOG_EMPTY_OBJS}: %.empty.o: %.c
-       ${CC} ${CFLAGS} ${GLOBUSTHRINC} -DPERF_EMPTY -c $< -o $@
+       ${CC} ${CFLAGS} -DPERF_EMPTY -c $< -o $@
 
 ${INTERLOG_PERF_OBJS}: %.perf.o: %.c
-       ${CC} ${CFLAGS} ${GLOBUSTHRINC} -c $< -o $@
+       ${CC} ${CFLAGS} -c $< -o $@
 
 #${INTERLOG_INLINE_EMPTY_OBJS}: %.io: %.c
-#      ${CC} ${CFLAGS} ${GLOBUSTHRINC} -DLB_PERF -DPERF_EMPTY -DPERF_EVENTS_INLINE -c $< -o $@
+#      ${CC} ${CFLAGS} -DLB_PERF -DPERF_EMPTY -DPERF_EVENTS_INLINE -c $< -o $@
 
 ${LOGD_NOBJS}: %.no: %.c
-       ${CC} ${CFLAGS} ${GLOBUSINC} -DLOGD_NOFILE -c $< -o $@
+       ${CC} ${CFLAGS} -DLOGD_NOFILE -c $< -o $@
 
 ${LOGD_OBJS}: %.o: %.c
-       ${CC} ${CFLAGS} ${GLOBUSINC} -c $< -o $@
+       ${CC} ${CFLAGS} -c $< -o $@
 
 logd_proto_test.o: %.o: %.c
-       ${CC} ${CFLAGS} ${GLOBUSINC} -c $< -o $@
+       ${CC} ${CFLAGS} -c $< -o $@
 
 ll_test.o: %.o: %.cpp
        ${CXX} ${CFLAGS} ${TEST_INC} -c $< -o $@
 
 il_test.o IlTestBase.o server_msgTest.o event_queueTest.o input_queue_socketTest.o event_storeTest.o: %.o: %.cpp
-       ${CXX} ${CFLAGS} ${GLOBUSTHRINC} ${TEST_INC} -c $< -o $@
+       ${CXX} ${CFLAGS} ${TEST_INC} -c $< -o $@
 
 clean:
-       rm -rf .libs/ *.o *.no ${LOGD} ${INTERLOGD} ${NOTIF_INTERLOGD} {MAN_GZ}
+       rm -rvf .libs/ *.o *.lo ${LOGD} ${INTERLOGD} ${NOTIF_INTERLOGD} ${MAN_GZ}
+       rm -rvf log.xml project/ rpmbuild/ RPMS/ tgz/
index 814f9b8..1454c9f 100755 (executable)
@@ -1,5 +1,21 @@
 #!/bin/sh
 # chkconfig: 345 76 24
+#
+# Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+# See http://www.eu-egee.org/partners/ for details on the copyright holders.
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 
 GLITE_LOCATION=${GLITE_LOCATION:-/opt/glite}
 GLITE_LOCATION_VAR=${GLITE_LOCATION_VAR:-$GLITE_LOCATION/var}
@@ -12,10 +28,36 @@ GLITE_LOCATION_VAR=${GLITE_LOCATION_VAR:-$GLITE_LOCATION/var}
 
 [ -f $HOME/.glite.conf ] && . $HOME/.glite.conf
 
-unset creds port
+LL_PIDFILE=${LL_PIDFILE:-$GLITE_LOCATION_VAR/glite-lb-logd.pid}
+IL_PIDFILE=${IL_PIDFILE:-$GLITE_LOCATION_VAR/glite-lb-interlogd.pid}
+
+unset creds port log4c
+
+start_daemon()
+{
+       local name="$1"
+       local pidfile="$2"
+       local cmd="$3"
+
+       if [ -f "$pidfile" ]; then
+               if kill -0 `cat $pidfile`; then
+                       return 0
+               fi
+               echo -n "Warning: stalled $pidfile for $name"
+               rm -f "$pidfile"
+       fi
+       echo -n "Starting $name ..."
+       su - $GLITE_USER -c "$log4c $cmd" && echo " done" || echo " FAILED"
+}
 
 start()
 {
+       case "$GLITE_LB_TYPE" in
+       proxy)
+               echo 'Not starting logd and interlogger, proxy only instance (GLITE_LB_TYPE=proxy).'
+               return 0
+       esac
+
        if test -z "$GLITE_USER" ;then
                echo 'Error: GLITE_USER is not set'
                echo FAILED
@@ -38,26 +80,50 @@ start()
        [ -n "$GLITE_LB_IL_SOCK" ] && sock="--socket $GLITE_LB_IL_SOCK"
        [ -n "$GLITE_LB_IL_FPREFIX" ] && fprefix="--file-prefix $GLITE_LB_IL_FPREFIX"
 
+       log4c="LOG4C_RCPATH='$GLITE_LOCATION/etc/glite-lb'"
+
        mkdir -p /var/glite/log 
        chown $GLITE_USER /var/glite/log
-       echo -n Starting glite-lb-logd ...
         (cd /tmp && ls -f /tmp |grep ^dglogd_sock_ |xargs rm -f)
-       su - $GLITE_USER -c "$GLITE_LOCATION/bin/glite-lb-logd \
-               $creds $port $sock $fprefix" && echo " done" || echo " FAILED"
+       start_daemon "glite-lb-logd" "$LL_PIDFILE" "$GLITE_LOCATION/bin/glite-lb-logd \
+               -i $LL_PIDFILE $creds $port $sock $fprefix"
+
+       start_daemon "glite-lb-interlogd" "$IL_PIDFILE" "$GLITE_LOCATION/bin/glite-lb-interlogd \
+               -i $IL_PIDFILE $creds $sock $fprefix"
+}
 
-       echo -n Starting glite-lb-interlogd ...
-       su - $GLITE_USER -c "$GLITE_LOCATION/bin/glite-lb-interlogd \
-               $creds $sock $fprefix" && echo " done" || echo " FAILED"
+killwait()
+{
+       pidfile=$1
+       if [ -f $pidfile ] && pid=`cat $pidfile` && kill $pid 2>/dev/null; then
+               cnt=0
+               while ps p $pid 2>/dev/null >/dev/null; do
+                       sleep 1;
+                       cnt=`expr $cnt + 1`
+                       if [ $cnt = 120 ]; then break; fi
+               done
+               if [ $cnt = 100 ]; then echo " can't stop"
+               else echo " done"; fi
+       else
+               echo " not running"
+       fi
 }
 
 stop()
 {
-               echo -n Stopping glite-lb-logd ...
-               killall glite-lb-logd
-               echo " done"
-               echo -n Stopping glite-lb-interlogd ...
-               killall glite-lb-interlogd
-               echo " done"
+       echo -n Stopping glite-lb-logd ...
+       killwait $LL_PIDFILE
+       echo -n Stopping glite-lb-interlogd ...
+       killwait $IL_PIDFILE
+
+       # for L&B <= 2.0
+       LC_ALL=C
+       if netstat -an --inet | grep "^tcp .* 0.0.0.0:${GLITE_LB_LOGGER_PORT:-9002} .*LISTEN" >/dev/null 2>&1 ;then
+               killall -9 glite-lb-logd
+       fi
+       if netstat -an --unix | grep "^unix .* LISTEN.* ${GLITE_LB_IL_SOCK:-/tmp/interlogger.sock}$" >/dev/null 2>&1 ;then
+               killall -9 glite-lb-interlogd
+       fi
 }
 
 status()
diff --git a/org.glite.lb.logger/configure b/org.glite.lb.logger/configure
new file mode 100755 (executable)
index 0000000..71ff6f0
--- /dev/null
@@ -0,0 +1,843 @@
+#!/usr/bin/perl
+
+# WARNING: Don't edit this file unless it is the master copy in org.glite.lb
+#
+# For the purpose of standalone builds of lb/jobid/lbjp-common components
+# it is copied on tagging 
+
+# $Header$
+#
+# Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+# See http://www.eu-egee.org/partners/ for details on the copyright holders.
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+use Getopt::Long;
+
+my $pwd = `pwd`; chomp $pwd;
+my $prefix = $pwd.'/stage';
+my $stagedir;
+my $staged;
+my $module;
+my $thrflavour = 'gcc64dbgpthr';
+my $nothrflavour = 'gcc64dbg';
+my $mode = 'build';
+my $help = 0;
+my $listmodules;
+my $version;
+my $branch;
+my $output;
+my $lb_tag = '';
+my $lbjp_tag = '';
+my $jp_tag = '';
+my $sec_tag = '';
+my $jobid_tag = '';
+my $libdir = getlibdir();
+
+my @nodes = qw/client server logger utils client-java doc ws-test db jpprimary jpindex jpclient harvester/;
+my %enable_nodes;
+my %disable_nodes;
+
+my %extern_prefix = (
+       cares => '/opt/c-ares',
+       classads => '/opt/classads',
+       cppunit => '/usr',
+       expat => '/usr',
+       globus => '/opt/globus',
+       gsoap => '/usr',
+       mysql => '/usr',
+       'mysql-devel' => '',
+       'mysql-server' => '',
+       voms => '/opt/glite',
+       gridsite => '/opt/glite',
+       lcas => '/opt/glite',
+       trustmanager => '/opt/glite',
+       utiljava => '/opt/glite',
+       ant => '/usr',
+       jdk => '/usr',
+       libtar => '/usr',
+       axis => '/usr',
+       log4c => '/usr',
+       postgresql => '/usr'
+);
+
+my %jar = (
+       'commons-codec' => '/usr/share/java/commons-codec.jar',
+       'commons-lang' => '/usr/share/java/commons-lang.jar',
+);
+
+
+my %glite_prefix;
+my %need_externs;
+my %need_externs_type;
+my %need_jars;
+my %extrafull;
+my %extranodmod;
+my %deps;
+my %deps_type;
+my %topbuild;
+
+my %lbmodules = (
+       'lb' => [ qw/client client-java common doc logger server state-machine types utils ws-interface ws-test harvester yaim glite-LB/], 
+       'security' => [qw/gss gsoap-plugin/],
+       'lbjp-common' => [qw/db log maildir server-bones trio jp-interface/],
+       'jobid' => [qw/api-c api-cpp api-java/],
+       'jp' => [ qw/client doc index primary server-common ws-interface/ ],
+       );
+
+
+my @opts = (
+       'prefix=s' => \$prefix,
+       'staged=s' => \$staged,
+       'module=s' => \$module,
+       'thrflavour=s' => \$thrflavour,
+       'nothrflavour=s' => \$nothrflavour,
+       'mode=s' => \$mode,
+       'listmodules=s' => \$listmodules,
+       'version=s' => \$version,
+       'branch=s' => \$branch,
+       'output=s' => \$output,
+       'stage=s' => \$stagedir,
+       'lb-tag=s' => \$lb_tag,
+       'lbjp-common-tag=s' => \$lbjp_tag,
+       'jp-tag=s' => \$jp_tag,
+       'security-tag=s' => \$sec_tag,
+       'jobid-tag=s' => \$jobid_tag,
+       'help' => \$help,
+       'libdir=s' => \$libdir,
+);
+
+for (@nodes) {
+       $enable_nodes{$_} = 0;
+       $disable_nodes{$_} = 0;
+       
+       push @opts,"disable-$_",\$disable_nodes{$_};
+       push @opts,"enable-$_",\$enable_nodes{$_};
+}
+
+push @opts,"with-$_=s",\$extern_prefix{$_} for keys %extern_prefix;
+push @opts,"with-$_=s",\$jar{$_} for keys %jar;
+
+my @keeparg = @ARGV;
+
+GetOptions @opts or die "Errors parsing command line\n";
+
+$extern_prefix{'mysql-devel'}=$extern_prefix{mysql} if $extern_prefix{'mysql-devel'} eq '';
+$extern_prefix{'mysql-server'}=$extern_prefix{mysql} if $extern_prefix{'mysql-server'} eq '';
+
+if ($help) { usage(); exit 0; }
+
+if ($listmodules) {
+       my @m = map "org.glite.$listmodules.$_",@{$lbmodules{$listmodules}};
+       print "@m\n";
+       exit 0;
+}
+
+warn "$0: --version, --branch and --output make sense only in --mode=etics\n"
+       if ($version || $output || $branch) && $mode ne 'etics';
+
+my $en;
+for (keys %enable_nodes) { $en = 1 if $enable_nodes{$_}; }
+
+my $dis;
+for (keys %disable_nodes) { $dis = 1 if $disable_nodes{$_}; }
+
+die "--enable-* and --disable-* are mutually exclusive\n"
+       if $en && $dis;
+
+die "--module cannot be used with --enable-* or --disable-*\n"
+       if $module && ($en || $dis);
+
+die "$module: unknown module\n" if $module && ! grep $module,@{$lbmodules{lb}},@{$lbmodules{security}},{$lbmodules{jp}};
+
+if ($dis) {
+       for (@nodes) {
+               $enable_nodes{$_} = 1 unless $disable_nodes{$_};
+       }
+}
+
+if (!$en && !$dis) { $enable_nodes{$_} = 1 for (@nodes) } ;
+
+for (keys %enable_nodes) { delete $enable_nodes{$_} unless $enable_nodes{$_}; }
+
+$stagedir = $prefix unless $stagedir;
+
+if ($mode eq 'build') {
+       print "Writing config.status\n";
+       open CONF,">config.status" or die "config.status: $!\n";
+       print CONF "$0 @keeparg\n";
+       close CONF;
+}
+
+
+my @modules;
+my %aux;
+
+if ($module) {
+#      push @modules,split(/[,.]+/,$module);
+       push @modules,$module;
+}
+else {
+       @modules = map(($extranodmod{$_} ? $extranodmod{$_} : 'lb.'.$_),(keys %enable_nodes));
+       
+       my $n;
+
+       do {
+               local $"="\n";
+               $n = $#modules;
+               push @modules,(map @{$deps{$_}},@modules);
+
+               undef %aux; @aux{@modules} = (1) x ($#modules+1);
+               @modules = keys %aux;
+       } while ($#modules > $n);
+}
+
+@aux{@modules} = (1) x ($#modules+1);
+delete $aux{$_} for (split /,/,$staged);
+@modules = keys %aux;
+
+mode_build() if $mode eq 'build';
+mode_checkout() if $mode eq 'checkout';
+mode_etics($module) if $mode eq 'etics';
+
+sub mode_build {
+       print "\nBuilding modules: @modules\n";
+       
+       my @ext = map @{$need_externs{$_}},@modules;
+       my @myjars = map @{$need_jars{$_}},@modules;
+       undef %aux; @aux{@ext} = 1;
+       @ext = keys %aux;
+       undef %aux; @aux{@myjars} = (1) x ($#myjars+1);
+       @myjars = keys %aux;
+       
+       print "\nRequired externals:\n";
+       print "\t$_: $extern_prefix{$_}\n" for @ext;
+       print "\t$_: $jar{$_}\n" for @myjars;
+       print "\nThis is a poor-man configure, it's up to you to have sources and externals there\n\n";
+       
+       mkinc($_) for @modules;
+       
+       print "Creating Makefile\n";
+       
+       open MAK,">Makefile" or die "Makefile: $!\n";
+       
+       print MAK "all: @modules\n\nclean:\n";
+       
+       for (@modules) {
+               my $full = full($_);
+               my $build = $topbuild{$_} ? '': '/build';
+               print MAK "\tcd $full$build && \${MAKE} clean\n"
+       }
+       
+       print MAK "\ndistclean:\n";
+       
+       for (@modules) {
+               my $full = full($_);
+               print MAK $topbuild{$_} ?
+                       "\tcd $full$build && \${MAKE} distclean\n" :
+                       "\trm -rf $full$build\n"
+       }
+       
+       print MAK "\n";
+       
+       for (@modules) {
+               my %ldeps; undef %ldeps;  
+               @ldeps{@{$deps{$_}}} = 1;
+               for my $x (split /,/,$staged) { delete $ldeps{$x}; }
+               my @dnames = $module ? () : keys %ldeps;
+       
+               my $full = full($_);
+               my $build = $topbuild{$_} ? '': '/build';
+       
+               print MAK "$_: @dnames\n\tcd $full$build && \${MAKE} && \${MAKE} install\n\n";
+       }
+       
+       close MAK;
+}
+       
+sub mode_checkout() {
+       for (@modules) {
+               my $module = $_;
+               my $tag = "";
+               if ($lb_tag){
+                       for (@{$lbmodules{lb}}){
+                               if ("lb.".$_ eq $module){
+                                       $tag = '-r '.$lb_tag;
+                               }
+                       }       
+               }
+               if ($lbjp_tag){
+                       for (@{$lbmodules{'lbjp-common'}}){
+                               if ("lbjp-common.".$_ eq $module){
+                                        $tag = '-r '.$lbjp_tag;
+                                }
+                       }
+               }
+               if ($jp_tag){
+                       for (@{$lbmodules{'jp'}}){
+                               if ("jp.".$_ eq $module){
+                                        $tag = '-r '.$jp_tag;
+                               }
+                        }
+               }
+               if ($sec_tag){
+                       for (@{$lbmodules{security}}){
+                               if ("security.".$_ eq $module){
+                                        $tag = '-r '.$sec_tag;
+                                }
+                       }
+               }
+               if ($jobid_tag){
+                       for (@{$lbmodules{jobid}}){
+                               if ("jobid.".$_ eq $module){
+                                        $tag = '-r '.$jobid_tag;
+                                }
+                       }
+               }
+               #if (grep {"lb.".$_ eq $module} @{$lbmodules{lb}}){
+               #       print "found";
+               #}
+               $_ = full($_);
+               print "\n*** Checking out $_\n";
+               system("cvs checkout  $tag $_") == 0 or die "cvs checkout $tag $_: $?\n";
+       }
+}
+
+BEGIN{
+%need_externs_aux = (
+       'lb.client' => [ qw/cppunit:B classads/ ],
+       'lb.client-java' => [ qw/ant:B jdk:B axis:B trustmanager utiljava/ ],
+       'lb.common' => [ qw/expat cares:B cppunit:B classads/ ],
+       'lb.doc' => [],
+       'lb.logger' => [ qw/cppunit:B/ ],
+       'lb.server' => [ qw/globus_essentials:R globus:B expat cares mysql:R mysql-server:R mysql-devel:B cppunit:B gsoap:B classads voms lcas gridsite/ ],
+       'lb.state-machine' => [ qw/classads/ ],
+       'lb.utils' => [ qw/cppunit:B/ ],
+       'lb.ws-interface' => [],
+       'lb.ws-test' => [ qw/gsoap:B/ ],
+       'lb.types' => [ qw// ],
+       'lb.harvester' => [ qw// ],
+       'lb.yaim' => [ qw/yaim_core:R/ ],
+       'lb.glite-LB' => [ qw/fetchcrl:R gpt:R gip_release:R gip_service:R bdii:R glite_version:R glite_info_templates:R glue_schema:R/ ],
+       'lbjp-common.db' => [ qw/mysql:B mysql-devel:B postgresql:B/ ],
+       'lbjp-common.log' => [ qw/log4c/ ],
+       'lbjp-common.maildir' => [ qw// ],
+       'lbjp-common.server-bones' => [ qw// ],
+       'lbjp-common.trio' => [ qw/cppunit:B/ ],
+       'lbjp-common.jp-interface' => [ qw/cppunit:B log4c:B/ ],
+       'security.gss' =>  [ qw/globus_essentials:R globus:B cares cppunit:B/ ],
+       'security.gsoap-plugin' =>  [ qw/cppunit:B globus_essentials:R globus:B cares:B gsoap:B/ ],
+       'jobid.api-c' =>  [ qw/cppunit:B/ ],
+       'jobid.api-cpp' =>  [ qw/cppunit:B/ ],
+       'jobid.api-java' =>  [ qw/ant:B jdk:B/ ],
+       'jp.client' => [ qw/gsoap libtar globus_essentials:R globus:B/ ],
+        'jp.doc' => [],
+        'jp.index' => [ qw/gsoap globus_essentials:R globus:B/ ],
+        'jp.primary' => [ qw/classads gsoap libtar globus_essentials:R globus:B/ ],
+        'jp.server-common' => [],
+        'jp.ws-interface' => [],
+);
+
+for my $ext (keys %need_externs_aux) {
+       for (@{$need_externs_aux{$ext}}) {
+               /([^:]*)(?::(.*))?/;
+               push @{$need_externs{$ext}},$1;
+               my $type = $2 ? $2 : 'BR';
+               $need_externs_type{$ext}->{$1} = $type;
+       }
+}
+
+%need_jars = (
+       'jobid.api-java' => [ qw/commons-codec/ ],
+       'lb.client-java' => [ qw/commons-lang/ ],
+);
+
+for my $jar (keys %need_jars) {
+       for (@{$need_jars{$jar}}) {
+               $need_externs_type{$jar}->{$_} = 'BR';  # XXX
+       }
+}
+
+%deps_aux = (
+       'lb.client' => [ qw/
+               lb.types:B lb.common
+               lbjp-common.trio
+               jobid.api-cpp:B jobid.api-c
+               security.gss
+       / ],
+       'lb.client-java' => [ qw/
+               lb.types:B
+               lb.ws-interface:B
+               jobid.api-java
+       / ],
+       'lb.common' => [ qw/
+               jobid.api-cpp:B jobid.api-c
+               lb.types:B lbjp-common.trio security.gss
+       / ],
+       'lb.doc' => [ qw/lb.types:B/ ],
+       'lb.logger' => [ qw/
+               lbjp-common.trio
+               lbjp-common.log
+               jobid.api-c
+               lb.common
+               security.gss
+       / ],
+       'lb.server' => [ qw/
+               lb.ws-interface lb.types:B lb.common lb.state-machine
+               lbjp-common.db lbjp-common.server-bones lbjp-common.trio lbjp-common.maildir lbjp-common.log
+               jobid.api-c
+               security.gsoap-plugin security.gss
+       / ],
+       'lb.state-machine' => [ qw/lb.types:B lb.common lbjp-common.jp-interface security.gss/ ],
+       'lb.utils' => [ qw/
+               lbjp-common.jp-interface
+               jobid.api-c
+               lbjp-common.trio lbjp-common.maildir
+               lb.client lb.state-machine
+       / ],
+       'lb.ws-test' => [ qw/security.gsoap-plugin lb.ws-interface/ ],
+       'lb.ws-interface' => [ qw/lb.types:B/ ],
+       'lb.types' => [ qw// ],
+       'lb.harvester' => [ qw/
+               jobid.api-c lbjp-common.trio lbjp-common.db lb.common lb.client
+               security.gss lbjp-common.log
+       / ],
+       'lb.yaim' => [ qw// ],
+       'lb.glite-LB' => [ qw/
+               lb.logger:R lb.server:R lb.utils:R lb.doc:R
+               lb.ws-test:R lb.harvester:R lb.yaim:R
+       / ],
+       'lbjp-common.db' => [ qw/lbjp-common.trio lbjp-common.log/ ],
+       'lbjp-common.maildir' => [ qw// ],
+       'lbjp-common.server-bones' => [ qw/lbjp-common.log/ ],
+       'lbjp-common.trio' => [ qw// ],
+       'security.gss' =>  [ qw// ],
+       'security.gsoap-plugin' =>  [ qw/security.gss/ ],
+       'jobid.api-c' =>  [ qw// ],
+       'jobid.api-cpp' =>  [ qw/jobid.api-c/ ],
+       'jobid.api-java' =>  [ qw// ],
+
+       'lbjp-common.jp-interface' => [ qw/lbjp-common.db jobid.api-c/ ],
+
+       'jp.client' => [ qw/
+                jp.ws-interface
+                lbjp-common.jp-interface lbjp-common.maildir
+                jobid.api-c
+                security.gsoap-plugin
+        / ],
+       'jp.doc' => [ qw// ],
+       'jp.index' => [ qw/
+                jp.server-common jp.ws-interface
+                lbjp-common.jp-interface lbjp-common.trio lbjp-common.db lbjp-common.server-bones
+                security.gsoap-plugin
+        / ],
+       'jp.primary' => [ qw/
+                jobid.api-c
+                jp.server-common jp.ws-interface
+                lb.state-machine
+                lbjp-common.jp-interface lbjp-common.trio lbjp-common.db lbjp-common.server-bones
+                security.gsoap-plugin
+        / ],
+       'jp.server-common' => [ qw/ 
+                lbjp-common.jp-interface lbjp-common.db
+        / ],
+       'jp.ws-interface' => [ qw// ],
+);
+
+for my $ext (keys %deps_aux) {
+       for (@{$deps_aux{$ext}}) {
+               /([^:]*)(?::(.*))?/;
+               push @{$deps{$ext}},$1;
+               my $type = $2 ? $2 : 'BR';
+               $deps_type{$ext}->{$1} = $type;
+       }
+}
+
+
+%extrafull = ( gridsite=>'org.gridsite.core');
+
+#( java => 'client-java' );
+%extranodmod = (
+       db => 'lbjp-common.db',
+       jpprimary => 'jp.primary',
+       jpindex => 'jp.index',
+       jpclient => 'jp.client',
+);
+
+%obsoletes = (
+       'lb.yaim' => [ qq/glite-yaim-lb/ ],
+);
+
+my @t = qw/lb.client-java jobid.api-java lb.types/;
+@topbuild{@t} = (1) x ($#t+1);
+}
+
+sub full
+{
+       my $short = shift;
+       return $extrafull{$short} ? $extrafull{$short} : 'org.glite.'.$short;
+}
+
+sub mkinc
+{
+       my %aux;
+       undef %aux;
+       my @m=qw/
+lb.client lb.doc lb.state-machine lb.ws-interface lb.logger lb.types lb.common lb.server lb.utils lb.ws-test lb.client-java lb.harvester lb.yaim lb.glite-LB
+security.gss security.gsoap-plugin
+jobid.api-c jobid.api-cpp jobid.api-java
+lbjp-common.db lbjp-common.log lbjp-common.maildir lbjp-common.server-bones lbjp-common.trio lbjp-common.jp-interface
+jp.client jp.doc jp.index jp.primary jp.server-common jp.ws-interface
+/;
+       @aux{@m} = (1) x ($#m+1);
+
+       my $short = shift;
+       my $full = full $short;
+
+       unless ($aux{$short}) {
+               print "Makefile.inc not needed in $full\n";
+               return;
+       }
+
+       my $build = '';
+       
+       unless ($topbuild{$_}) {
+               $build = '/build';
+               unless (-d "$full/build") {
+                       mkdir "$full/build" or die "mkdir $full/build: $!\n";
+               }
+               unlink "$full/build/Makefile";
+               symlink "../Makefile","$full/build/Makefile" or die "symlink ../Makefile $full/build/Makefile: $!\n";
+       }
+
+       open MKINC,">$full$build/Makefile.inc"
+               or die "$full$build/Makefile.inc: $!\n";
+
+       print "Creating $full$build/Makefile.inc\n";
+
+       print MKINC qq{
+PREFIX = $prefix
+stagedir = $stagedir
+thrflavour = $thrflavour
+nothrflavour = $nothrflavour
+libdir = $libdir
+};
+
+       for (@{$need_externs{$short}}) {
+               print MKINC "${_}_prefix = $extern_prefix{$_}\n"
+       }
+
+       for (@{$need_jars{$short}}) {
+               print MKINC "${_}_jar = $jar{$_}\n"
+       }
+
+       my $need_gsoap = 0;
+       for (@{$need_externs{$short}})  { $need_gsoap = 1 if $_ eq 'gsoap'; }
+
+       print MKINC "gsoap_default_version=".gsoap_version()."\n"  if $need_gsoap;
+
+       close MKINC;
+}
+
+my %etics_externs;
+my %etics_projects;
+BEGIN{
+       %etics_externs = (
+               globus_essentials=>'vdt_globus_essentials',
+               globus=>'globus',
+               cares=>'c-ares',
+               voms=>'org.glite.security.voms-api-cpp',
+               gridsite=>'org.gridsite.shared',
+               lcas=>'org.glite.security.lcas',
+               trustmanager=>'org.glite.security.trustmanager',
+               utiljava=>'org.glite.security.util-java',
+               gpt=>'gpt',
+               fetchcrl=>'fetch-crl',
+               gip_release=>'glite-info-provider-release',
+               gip_service=>'glite-info-provider-service',
+               bdii=>'bdii',
+               glite_version=>'glite-version',
+               glite_info_templates=>'glite-info-templates',
+               glue_schema=>'glue-schema',
+               yaim_core=>'org.glite.yaim.core',
+       );
+       %etics_projects = (
+               vdt=>[qw/globus globus_essentials/],
+               'org.glite'=>[qw/voms gridsite lcas gpt gip_release gip_service bdii glite_version glite_info_templates glue_schema yaim_core/],
+       );
+};
+
+sub mode_etics {
+       $fmod = shift;
+
+       die "$0: --module required with --etics\n" unless $fmod;
+       
+       my ($subsys,$module) = split /\./,$fmod;
+
+       my ($major,$minor,$rev,$age);
+
+       if ($version) {
+               $version =~ /([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)-(.+)/;
+               ($major,$minor,$rev,$age) = ($1,$2,$3,$4);
+       }
+       else { 
+               open V,"org.glite.$subsys.$module/project/version.properties"
+                       or die "org.glite.$subsys.$module/project/version.properties: $!\n";
+       
+               while ($_ = <V>) {
+                       chomp;
+                       ($major,$minor,$rev) = ($1,$2,$3) if /module\.version\s*=\s*([[:digit:]]+)\.([[:digit:]]+)\.([[:digit:]]+)/;
+                       $age = $1 if /module\.age\s*=\s*([[:digit:]]+)/;
+               }
+               close V;
+       }
+
+       my @copts = ();
+       my %ge;
+       @ge{@{$etics_projects{'org.glite'}}} = (1) x ($#{$etics_projects{'org.glite'}}+1);
+
+       for (@{$need_externs{"$subsys.$module"}}) {
+           if ($need_externs_type{"$subsys.$module"}->{$_}=~/B/) {
+               my $eext = $etics_externs{$_} ? $etics_externs{$_} : $_;
+               push @copts,$ge{$_} ? "--with-$_=\${stageDir}" : "--with-$_=\${$eext.location}";
+           }
+       }
+
+       for (@{$need_jars{"$subsys.$module"}}) {
+               my $eext = $etics_externs{$_} ? $etics_externs{$_} : $_;
+
+               push @copts,"--with-$_ \${$eext.location}/$_*.jar";
+       }
+
+       my $conf;
+       my $conftag;
+
+       if ($branch) {
+               $conf = "glite-${subsys}-${module}_$branch"; 
+               $conftag = $branch; 
+               $dwpath = ""; }
+       else {
+               $conf = "glite-$subsys-${module}_R_${major}_${minor}_${rev}_${age}"; 
+               $conftag = $conf; 
+               $dwpath = "path = \${projectName}/\${moduleName}/\${version}/\${platformName}/\${packageName}-\${version}-\${age}.tar.gz\n"; }
+#      my $conf = "glite-$subsys-${module}_R_${major}_${minor}_${rev}_${age}";
+       my $file = $output ? $output : "$conf.ini";
+       open C,">$file" or die "$file: $!\n";
+
+       my $buildroot = $topbuild{"$subsys.$module"} ? '' : "build.root = build";
+
+       my $confdir = $topbuild{"$subsys.$module"} ? '..' : '../..';
+
+       my $package_description = "";
+       my $package_summary = "";
+
+       if (-e "org.glite.$subsys.$module/project/package.description") {
+               open V, "org.glite.$subsys.$module/project/package.description";
+               $package_description = join ("", <V>);
+               close V;
+               chomp $package_description;
+               $package_description =~ s/\n/\\n/g; 
+               $package_description = "package.description = $package_description";
+       } 
+       else { 
+               print STDERR "package.description not found for $subsys.$module!\n"; }
+
+       if (-e "org.glite.$subsys.$module/project/package.summary") {
+               open V, "org.glite.$subsys.$module/project/package.summary";
+               $package_summary = join ("", <V>);
+               close V;
+               chomp $package_summary;
+               $package_summary =~ s/\n/\\n/g; 
+               $package_summary = "package.summary = $package_summary";
+       } 
+       else { 
+               print STDERR "package.summary not found for $subsys.$module!\n"; }
+
+
+       print STDERR "Writing $file\n";
+       print C qq{
+[Configuration-$conf]
+profile = None
+moduleName = org.glite.$subsys.$module
+displayName = $conf
+description = org.glite.$subsys.$module
+projectName = org.glite
+age = $age
+deploymentType = None
+tag = $conftag
+version = $major.$minor.$rev
+$dwpath
+[Platform-default:VcsCommand]
+displayName = None
+description = None
+tag = cvs -d \${vcsroot} tag -R \${tag} \${moduleName}
+branch = None
+commit = None
+checkout = cvs -d \${vcsroot} co -r \${tag} \${moduleName}
+
+[Platform-default:BuildCommand]
+postpublish = None
+packaging = None
+displayName = None
+description = None
+doc = None
+prepublish = None
+publish = None
+compile = make
+init = None
+install = make install
+clean = make clean
+test = make check
+configure = cd $confdir && /usr/bin/perl \${moduleName}/configure --thrflavour=\${globus.thr.flavor} --nothrflavour=\${globus.nothr.flavor} --prefix=\${prefix} --stage=\${stageDir} --libdir=\${libdir} --module $subsys.$module @copts
+checkstyle = None
+
+[Platform-default:Property]
+$buildroot
+$package_description
+$package_summary
+};
+       for (@{$obsoletes{"$subsys.$module"}}) {
+               print C "package.obsoletes = $_\n";
+               print C "package.replaces = $_\n";
+       }
+
+       print C qq{
+[Platform-default:DynamicDependency]
+};
+       for (@{$need_externs{"$subsys.$module"}},@{$need_jars{"$subsys.$module"}}) {
+               my $eext = $etics_externs{$_} ? $etics_externs{$_} : $_;
+
+               my $proj = 'externals';
+               for my $p (keys %etics_projects) {
+                       for $m (@{$etics_projects{$p}}) {
+                               $proj = $p if $m eq $_;
+                       }
+               }
+
+               my $type = $need_externs_type{"$subsys.$module"}->{$_};
+               print C "$proj|$eext = $type\n";
+       }
+
+       for (@{$deps{"$subsys.$module"}}) {
+               my $type = $deps_type{"$subsys.$module"}->{$_};
+               print C "org.glite|org.glite.$_ = $type\n";
+       }
+
+       close C;
+}
+
+sub gsoap_version {
+       local $_;
+       my $gsoap_version;
+       open S,"$extern_prefix{gsoap}/bin/soapcpp2 -v 2>&1 |" or die "$extern_prefix{gsoap}/bin/soapcpp2: $!\n";
+
+       while ($_ = <S>) {
+               chomp;
+
+               $gsoap_version = $1 if /The gSOAP Stub and Skeleton Compiler for C and C\+\+ ([.[:digit:][:alpha:]]+)$/;
+       }
+       close S;
+       return $gsoap_version;
+}
+
+sub getlibdir {
+       if ( -e "/etc/debian_version") { # We are on Debian
+               $lib64="lib";
+               $lib32="lib32"; }
+       else { # Another distribution
+               $lib64="lib64";
+               $lib32="lib";   }
+        $libdir=$lib32;
+
+        open INP, "uname -s | "; # Check kernel name
+        $kname= <INP>;
+        chomp($kname);
+        close INP;
+
+        if ( $kname == "Linux") {
+                $arch = ("x86_64\npowerpc\nppc64\n");
+
+                open INP, "uname -p | "; # Check processor type
+                $procname= <INP>;
+                chomp($procname);
+                close INP;
+
+                if ($arch =~/^$procname\n/) {
+                        return ($lib64); }
+
+                open INP, "uname -m | "; # Check machine hardware
+                $machname= <INP>;
+                chomp($machname);
+                close INP;
+
+                if ($arch =~/^$machname\n/) {
+                        return ($lib64); }
+
+                # special cases (hyperlink lib64, Debian)
+                if (-l "/usr/lib64") {
+                        $libdir=$lib32; }
+
+                # if /usr/lib64 doesn't exist at all (AIX)
+                unless ( -e "/usr/lib64" ) {
+                        $libdir=$lib32; }
+        }
+
+        if ( $kname == "SunOS") {
+                if (-e "/usr/lib/64") {
+                $libdir="lib/64"; }
+        }
+
+        return $libdir;
+}
+
+sub usage {
+       my @ext = keys %extern_prefix;
+       my @myjars, keys %jar;
+
+       print STDERR qq{
+usage: $0 options
+
+General options (defaults in []):
+  --prefix=PREFIX              destination directory [./stage]
+  --staged=module,module,...   what is already in PREFIX (specify without org.glite.)
+  --thrflavour=flavour
+  --nothrflavour=flavour       threaded and non-treaded flavours [gcc64dbgpthr,gcc64dbg]
+  --listmodules=subsys          list modules of a subsystem
+  --libdir=libdir              typically [lib,lib64] postfix
+  
+Mode of operation:
+  --mode={checkout|build|etics}        what to do [build]
+  
+What to build:
+  --module=module              build this module only (mostly in-Etics operation)
+  --enable-NODE                        build this "node" (set of modules) only. Available nodes are
+                                       @{$lbmodules{lb}},@{$lbmodules{security}}
+  --disable-NODE               don't build this node
+  --lb-tag=tag                 checkout LB modules with specific tag
+  --jp-tag=tag                 checkout JP modules with specific tag
+  --lbjp-common-tag=tag         checkout lbjp-common modules with specific tag
+  --security-tag=tag           checkout security modules with specific tag
+  --jobid-tag=tag              checkout jobid modules with specific tag
+
+Dependencies:
+  --with-EXTERNAL=PATH         where to look for an external. Required externals
+                               (not all for all modules) are:
+                                       @ext
+  --with-JAR=JAR               where to look for jars. Required jars are:
+                                       @myjars
+                               Summary of what will be used is always printed
+
+};
+
+}
index 20732a0..68ae914 100644 (file)
@@ -1,4 +1,4 @@
-.TH EDG-WL-INTERLOGD 8 "May 2003" "EU DataGrid Project" "Logging&Bookkeeping"
+.TH GLITE-LB-INTERLOGD 8 "April 2008" "EU EGEE Project" "Logging&Bookkeeping"
 
 .SH NAME
 glite-lb-interlogd - interlogger daemon
@@ -68,6 +68,10 @@ environment variable.
 Don't run as daemon (do not fork and put itself into background).
 
 .TP
+.BI \-i " FILE" "\fR,\fP --pidfile " FILE
+Store process id into this file rather than default /var/glite/glite-lb-[notif]-interlogd.pid
+
+.TP
 .BI \-f " PREFIX" "\fR,\fP --file-prefix " PREFIX
 .I PREFIX 
 is path prefix of the event files.
@@ -120,6 +124,10 @@ Be lazy when closing connections to servers (default,
 .I TIMEOUT\fR
 ==0 means turn lazy off).
 
+.TP
+.BI "-h\fR,\fP --help"
+Print help and exit.
+
 .\".SH USAGE
 .\" Add any additional description here
 
@@ -130,10 +138,10 @@ Be lazy when closing connections to servers (default,
 .I /tmp/interlogger.sock
 Default name of local socket.
 .TP
-.I /tmp/dglogd.log*
+.I /var/glite/log/dglogd.log*
 Default location of event files.
 
-.I /tmp/dglogd.log*.ctl
+.I /var/glite/log/dglogd.log*.ctl
 Interlogger's control files keeping the information on status of
 the corresponding event file wrt. delivery to the target server.
 
@@ -166,10 +174,10 @@ If
 is set, it is used to locate proxy certificate file.
 
 .SH REPORTING BUGS
-Please, report all bugs to EU DataGrid Bug Tracking System located at http://marianne.in2p3.fr/datagrid/bugzilla
+Please, report all bugs to EU EGEE Bug Tracking System located at https://savannah.cern.ch
 
 .SH SEE ALSO
 .B te-lb-bkserverd\fR(8),\fP glite-lb-logd\fR(8),\fP glite-lb-logevent\fR(1),\fP
 
 .SH AUTHOR
-EU DataGrid Work Package 1, CESNET group.
+EU EGEE, JRA1.
index c9bf108..2a04be6 100644 (file)
@@ -1,4 +1,4 @@
-.TH EDG-WL-LOGD 8 "May 2003" "EU DataGrid Project" "Logging&Bookkeeping"
+.TH GLITE-LB-LOGD 8 "April 2008" "EU EGEE Project" "Logging&Bookkeeping"
 
 .SH NAME
 glite-lb-logd - local logger daemon
@@ -82,10 +82,20 @@ Send the messages to interlogger through the UNIX socket
 The value has to be same as used in the cooperating glite-lb-interlogd.
 
 .TP
+.BI \-i " FILE" "\fR,\fP --pidfile " FILE
+Store pid into 
+.I FILE\fR.\fP
+Defaults to /var/glite/glite-lb-logd.pid.
+
+.TP
 .B "-V\fR,\fP --version"
 Print version and exit.
 
 .TP
+.B "-h\fR,\fP --help"
+Print help and exit.
+
+.TP
 .B --noAuth
 Don't require valid X509 credentials to run the daemon.
 Used for debugging only.
@@ -111,7 +121,7 @@ Dangerous, for debugging only! Don't use at all.
 Default name of local socket.
 
 .TP
-.I /tmp/dglogd.log*
+.I /var/glite/log/dglogd.log*
 Default location of the event storage files.
 .TP
 No configuration files needed.
@@ -153,10 +163,10 @@ Decrease verbosity of the program.
 
 
 .SH REPORTING BUGS
-Please, report all bugs to DataGrid Bug Tracking System located at http://marianne.in2p3.fr/datagrid/bugzilla
+Please, report all bugs to EGEE Bug Tracking System located at https://savannah.cern.ch
 
 .SH SEE ALSO
 .B glite-lb-bkserverd\fR(8),\fP glite-lb-interlogd\fR(8),\fP glite-lb-logevent\fR(1),\fP
 
 .SH AUTHOR
-EU DataGrid Work Package 1, CESNET group.
+EU EGEE, JRA1.
diff --git a/org.glite.lb.logger/interface/il_error.h b/org.glite.lb.logger/interface/il_error.h
new file mode 100644 (file)
index 0000000..f150212
--- /dev/null
@@ -0,0 +1,49 @@
+#ifndef IL_ERROR_H
+#define IL_ERROR_H
+
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <syslog.h>
+
+enum err_code_maj { /* minor =                   */
+  IL_OK,            /*     0                     */
+  IL_SYS,           /*     errno                 */
+  IL_NOMEM,         /*     ENOMEM                */
+  IL_PROTO,         /*     LB_*                  */
+  IL_LBAPI,         /*     dgLBErrCode           */
+  IL_DGGSS,         /*     EDG_WLL_GSS_*         */
+  IL_HOST,          /*     h_errno               */
+  IL_DL             /*     dlerror               */
+};
+
+struct error_inf {
+  int  code_maj;
+  long code_min;
+  char *msg;
+};
+
+int init_errors();
+int set_error(int, long, char *);
+int clear_error();
+int error_get_maj();
+long error_get_min();
+char *error_get_msg();
+
+#endif
similarity index 67%
rename from org.glite.lb.logger/src/interlogd.h
rename to org.glite.lb.logger/interface/interlogd.h
index cf4d634..bdf0146 100644 (file)
@@ -2,10 +2,32 @@
 #define INTERLOGGER_P_H
 
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 
 #include "il_error.h"
 #include "glite/security/glite_gss.h"
 #include "glite/lb/il_msg.h"
+#include "glite/lbu/log.h"
 
 #include <pthread.h>
 #include <sys/time.h>
@@ -42,6 +64,7 @@
 #define IL_EVENT_GET_UNIQUE(a) edg_wll_NotifIdGetUnique((a))
 #define IL_EVENT_ID_FREE(a)    edg_wll_NotifIdFree((a))
 #define IL_EVENT_ID_PARSE(a,b) edg_wll_NotifIdParse((a),(b))
+#define IL_LOG_CATEGORY        LOG_CATEGORY_LB_ILNOTIF
 
 #else
 
@@ -52,6 +75,7 @@
 #define IL_EVENT_GET_UNIQUE(a) edg_wlc_JobIdGetUnique((a))
 #define IL_EVENT_ID_FREE(a)    edg_wlc_JobIdFree((a))
 #define IL_EVENT_ID_PARSE(a,b) edg_wlc_JobIdParse((a),(b))
+#define IL_LOG_CATEGORY        LOG_CATEGORY_LB_IL
 
 #endif
 
 
 // #define TIMEOUT      5
 extern int TIMEOUT;
-#define INPUT_TIMEOUT (60)
+#ifdef LB_PERF
+#define INPUT_TIMEOUT (1)
+#define EXIT_TIMEOUT  (20)
+#else
+#define INPUT_TIMEOUT (5)
 #define EXIT_TIMEOUT (1*60)
+#endif
+#define RECOVER_TIMEOUT (60)
+
+typedef struct cred_handle {
+       edg_wll_GssCred creds;
+       int counter;
+} cred_handle_t;
+extern cred_handle_t *cred_handle;
 
-extern gss_cred_id_t cred_handle;
 extern pthread_mutex_t cred_handle_lock;
+extern pthread_key_t cred_handle_key;
 extern char *cert_file;
 extern char *key_file;
 extern char *CAcert_dir;
@@ -72,6 +108,10 @@ extern int bs_only;
 extern int killflg;
 extern int lazy_close;
 extern int default_close_timeout;
+extern size_t max_store_size;
+extern size_t queue_size_high;
+extern size_t queue_size_low;
+extern int parallel;
 #ifdef LB_PERF
 extern int nosend, nosync, norecover, noparse;
 #ifdef PERF_EVENTS_INLINE
@@ -84,7 +124,30 @@ extern char *event_source;
 extern pthread_mutex_t flush_lock;
 extern pthread_cond_t flush_cond;
 #endif
-  
+
+typedef struct {
+       /* il_octet_string_t */
+       int       len;
+       char     *data;
+       /* http message specific */
+       enum { IL_HTTP_OTHER,
+              IL_HTTP_GET,
+              IL_HTTP_POST,
+              IL_HTTP_REPLY
+       } msg_type;
+       int       reply_code;
+       char      *reply_string;
+       size_t    content_length;
+       char     *host;
+} il_http_message_t;
+
+/* this struct can be passed instead of il_octet_string as parameter */
+typedef union {
+       il_octet_string_t bin_msg;
+       il_http_message_t http_msg;
+} il_message_t;
+
+
 struct event_store {
        char     *event_file_name;         /* file with events from local logger */
        char     *control_file_name;       /* file with control information */
@@ -93,8 +156,11 @@ struct event_store {
        long      last_committed_ls;       /*  -"-                                           LS */
        long      offset;                  /* expected file position of next event */
        time_t    last_modified;           /* time of the last file modification */
-       int       recovering;              /* flag for recovery mode */
-       pthread_rwlock_t update_lock;      /* lock to prevent simultaneous updates */
+       int       generation;              /* cleanup counter, scopes the offset */
+       long long                 rotate_index;                    /* rotation counter */
+       struct  event_store_list *le;      /* points back to the list */
+       pthread_rwlock_t commit_lock;      /* lock to prevent simultaneous updates to last_committed_* */
+       pthread_rwlock_t offset_lock;      /* lock to prevent simultaneous updates offset */
        pthread_rwlock_t use_lock;         /* lock to prevent struct deallocation */
 #if defined(IL_NOTIFICATIONS)
        char     *dest;                    /* host:port destination */
@@ -109,6 +175,7 @@ struct server_msg {
        int                     len;
        int                     ev_len;
        struct event_store     *es;             /* cache for corresponding event store */
+       int                     generation;     /* event store genereation */
        long                    receipt_to;     /* receiver (long local-logger id - LLLID) of delivery confirmation (for priority messages) */
 #if defined(IL_NOTIFICATIONS)
        char                   *dest_name;
@@ -123,6 +190,7 @@ struct event_queue {
        edg_wll_GssConnection   gss;            /* GSS connection */
        char                   *dest_name;
        int                     dest_port;
+       char                       *dest;
        int                     timeout;        /* queue timeout */
        struct event_queue_msg *tail;           /* last message in the queue */
        struct event_queue_msg *head;           /* first message in the queue */
@@ -144,8 +212,25 @@ struct event_queue {
        int                     times_empty;    /* number of times the queue was emptied */
        int                     max_len;        /* max queue length */
        int                     cur_len;        /* current length */
+       int                                             throttling;             /* event insertion suspend flag */
+       int                                             first_event_sent; /* connection can be preempted by server */
+       /* delivery methods */
+       int             (*event_queue_connect)(struct event_queue *);
+       int             (*event_queue_send)(struct event_queue *);
+       int             (*event_queue_close)(struct event_queue *);
+       void                               *plugin_data;        /* opaque data used by output plugins */
+};
+
+struct il_output_plugin {
+       int     (*event_queue_connect)(struct event_queue *);
+       int     (*event_queue_send)(struct event_queue *);
+       int     (*event_queue_close)(struct event_queue *);
+       int             (*plugin_init)(char *);
+       int             (*plugin_supports_scheme)(const char *);
 };
 
+/* credential destructor */
+void cred_handle_destroy(void *);
 
 /* server msg methods */
 struct server_msg *server_msg_create(il_octet_string_t *, long);
@@ -157,7 +242,7 @@ int server_msg_is_priority(struct server_msg *);
 int server_msg_free(struct server_msg *);
 
 /* general event queue methods */
-struct event_queue *event_queue_create(char *);
+struct event_queue *event_queue_create(char *, struct il_output_plugin *);
 int event_queue_free(struct event_queue *);
 int event_queue_empty(struct event_queue *);
 int event_queue_insert(struct event_queue *, struct server_msg *);
@@ -166,7 +251,7 @@ int event_queue_remove(struct event_queue *);
 int event_queue_enqueue(struct event_queue *, char *);
 /* helper */
 int enqueue_msg(struct event_queue *, struct server_msg *);
-int event_queue_move_events(struct event_queue *, struct event_queue *, int (*)(struct server_msg *, void *), void *); 
+int event_queue_move_events(struct event_queue *, struct event_queue *, int (*)(struct server_msg *, void *), void *);
 
 /* protocol event queue methods */
 int event_queue_connect(struct event_queue *);
@@ -189,7 +274,7 @@ int event_queue_cond_unlock(struct event_queue *);
 /* input queue */
 int input_queue_attach();
 void input_queue_detach();
-int input_queue_get(il_octet_string_t *, long *, int);
+int input_queue_get(il_octet_string_t **, long *, int);
 
 /* queue management functions */
 int queue_list_init(char *);
@@ -209,18 +294,33 @@ int notifid_map_set_expiration(const char *, time_t);
 int event_store_init(char *);
 int event_store_cleanup();
 int event_store_recover_all(void);
-struct event_store *event_store_find(char *);
+struct event_store *event_store_find(char *, const char *);
 int event_store_sync(struct event_store *, long);
 int event_store_next(struct event_store *, long, int);
-int event_store_commit(struct event_store *, int, int);
+int event_store_commit(struct event_store *, int, int, int);
 int event_store_recover(struct event_store *);
 int event_store_release(struct event_store *);
 /* int event_store_remove(struct event_store *); */
 
+#if defined(IL_WS)
+/* http functions */
+int parse_header(const char *, il_http_message_t *);
+int receive_http(void *, int (*)(void *, char *, const int), il_http_message_t *);
+#endif
+
+/* plugin functions */
+int plugin_mgr_init(const char *, char *);
+struct il_output_plugin *plugin_get(const char *);
+
 /* master main loop */
 int loop();
+void do_handle_signal();
 
 /* recover thread */
 void *recover_thread(void*);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
index 2b299cd..a4bee80 100644 (file)
 - fixed sending empty events
 - do not syslog "error reading server reply" unnecessarily
 
-1.4.11-1
-- Support chconfig in startup scripts (#27055)
-- Proper kill signal handling (#36470)
+2.0.0-1
+- LB 2.0 release
 
-1.4.11-2
-- L&B server export to MSG
+2.0.0-2
+- fixed configure to work in etics
 
-1.4.11-3
-- Module Repacked
+2.0.0-3
+- Fixed typos in the Makefile
 
-1.4.11-4
-- Module rebuilt
+2.0.0-4
+- configure script update (globus flavors added to configure call)
+
+2.0.1-1
+- (from 1.4.11-1) Support chconfig in startup scripts (#27055)
+- (from 1.4.11-1) Proper kill signal handling (#36470)
+
+2.0.2-1
+- implemented multi-file event store, avoiding ever-growing files
+  in the case of heavy traffic notifications
+
+2.0.3-1
+- Fixed handling messages with destination not set
+- Additional logging output
+
+2.0.4-1
+- Man page update
+
+2.0.4-2
+- install libraries into $libdir
+
+2.1.0-1
+- Compliance with the Common Logging Format
+- Fixed startup script (Savannah Bug #29081)
+- Addressed memory consumption (Savannah Bug #48164)
+- Improved portability
+- IPv6 fixes
+- SIGHUP handling
+- Warnings on expected server connection disconnections removed
+- Database and permission errors from server handled by wait&retry
+
+2.1.1-1
+- Startup script update
+
+2.1.2-1
+- Startup script update: log4c config file name hardcoded to 'log4crc'
+
+2.1.3-1
+- Configuration files moved from /etc to /etc/lb
+- LCAS logging temporarily disabled
+2.1.4-1
+- Introduce a separate logging category for notification interlogger
+- Fix problem with dropping unused notoifications
 
diff --git a/org.glite.lb.logger/project/package.description b/org.glite.lb.logger/project/package.description
new file mode 100644 (file)
index 0000000..cd0621b
--- /dev/null
@@ -0,0 +1 @@
+glite-lb-logger is the gLite LB local-logger and inter-logger. This package contains the local-logger (glite-lb-logd), inter-logger (glite-lb-interlogd) and notification inter-logger (glite-lb-notif-interlogd) daemons.
diff --git a/org.glite.lb.logger/project/package.summary b/org.glite.lb.logger/project/package.summary
new file mode 100644 (file)
index 0000000..089b630
--- /dev/null
@@ -0,0 +1 @@
+gLite Logging and Bookkeeping local-logger and inter-logger
index 8a32c13..6e8c734 100644 (file)
@@ -1,3 +1,3 @@
 # $Header$
-module.version=1.4.11
-module.age=4
+module.version=2.1.4
+module.age=1
index 692c019..0690174 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _CONNECTION_H
 #define _CONNECTION_H
 
@@ -8,6 +25,8 @@ public:
        public:
                virtual Connection *newConnection(int fd) const = 0;
                virtual Connection *accept(int fd) const = 0;
+
+               virtual ~Factory() {}
        };
 
        class Endpoint {
diff --git a/org.glite.lb.logger/src-nt/Connection.cpp b/org.glite.lb.logger/src-nt/Connection.cpp
new file mode 100644 (file)
index 0000000..2d0938d
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "Connection.H"
+
+Connection::Factory::~Factory() {
+}
index 1fa4cab..840343b 100644 (file)
-#ifndef _EVENT_MANAGER_H
-#define _EVENT_MANAGER_H
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
 
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
 
-class EventManager {
-public:
-       // type for return code of event handler
-       typedef enum {
-               NOT_HANDLED,  // the event was not handled at all
-               HANDLED,      // the event was handled succesfully
-               HANDLED_FINAL // the event was handled, 
-               // no other handlers should be called
-       } eventstatus_t;
+    http://www.apache.org/licenses/LICENSE-2.0
 
-       
-       static EventManager* getEventManager() { return &theEventManager; };
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
 
-       class Event {
-       public:
-       };
+#ifndef _EVENT_MANAGER_H
+#define _EVENT_MANAGER_H
 
-       template<class T>
-       class EventHandler {
-       public:
+#include <list>
 
-               virtual eventstatus_t handleEvent(T *&e);
 
-               eventstatus_t dispatchEvent(Event *&e) {
-                       T *event = dynamic_cast<T*>(e);
-                       
-                       if(event) 
-                               return(handleEvent(event));
-                       else
-                               return(NOT_HANDLED);
-               }
-       };
+// interface
 
+/**
+ * Base class of event hierarchy.
+ */
+class Event {
+public:
+    virtual ~Event() {}
+};
 
-       void postEvent(Event &);
 
-       template<class T>
-       bool registerHandler(EventHandler<T> *);
+/**
+ * Base class for event handler address.
+ */
+class EventHandler {
+public:
+    // constants for event handler return codes
+    static const int NOT_HANDLED = 0;
+    static const int HANDLED = 1;
+    static const int HANDLED_FINAL = 2;
+    static const int HANDLED_NEW = 3;
+
+    virtual int handleEvent(Event* &e) { return NOT_HANDLED; }
+    virtual ~EventHandler() {}
+};
 
-       template<class T>
-       bool registerHandlerFirst(EventHandler<T> *);
 
+/**
+ * Holds addres of event handler, ie. pointer to member function of T 
+ * that takes E* & as an argument.
+ */
+template<class T, class E>
+class TypedEventHandler: public EventHandler {
+public:
+    typedef int (T::*handlerType)(E* &);
+    
+    TypedEventHandler(T *handler, handlerType method) 
+       : m_handler(handler), m_handleEvent(method) {
+    }
+
+    virtual int handleEvent(Event* &e) {
+       E *ne = dynamic_cast<E*>(e);
+       int result = EventHandler::NOT_HANDLED;
+       if(ne) {
+           result = (m_handler->*m_handleEvent)(ne);
+           if((result == EventHandler::HANDLED_NEW) && 
+              !(ne == e)) {
+               delete e;
+               e = ne;
+           }
+       }
+       return result;
+    }
+    
 private:
-       
-       // the event manager
-       static EventManager theEventManager;
+    T *m_handler;
+    handlerType m_handleEvent;
+};
+
 
-       // private default constructor for singleton instance
-       EventManager() 
-               {};
+class EventManager {
+public:
+    
+    int postEvent(Event* &event);
+    
+    template<class T, class E>
+    EventHandler& registerHandler(T *handler, int (T::*method)(E* &)) {
+       EventHandler *h = new TypedEventHandler<T,E>(handler, method);
+       addHandler(h);
+       return *h;
+    }
+    
+    template<class T>
+    EventHandler& registerHandler(T *handler) {
+       return registerHandler(handler, &T::handleEvent);
+    }
+
+private:
+    std::list<EventHandler*> handlers;
 
+    void addHandler(EventHandler*);
+    void removeHandler(EventHandler *);
 };
 
 
diff --git a/org.glite.lb.logger/src-nt/EventManager.cpp b/org.glite.lb.logger/src-nt/EventManager.cpp
new file mode 100644 (file)
index 0000000..16b577b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "EventManager.H"
+
+int
+EventManager::postEvent(Event* &e)
+{
+  for(std::list<EventHandler*>::iterator i = handlers.begin();
+      i != handlers.end();
+      i++) {
+    (*i)->handleEvent(e);
+  }
+  return 0;
+}
+
+void
+EventManager::addHandler(EventHandler *handler)
+{
+  handlers.push_back(handler);
+}
+
+void
+EventManager::removeHandler(EventHandler *handler)
+{
+}
index 0fbac3e..678e258 100644 (file)
@@ -1,7 +1,30 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _EXCEPTION_H
 #define _EXCEPTION_H
 
 class Exception {
 };
 
+class FatalException {
+};
+
+
+#define E_ASSERT(a) if(!(a)) { throw new FatalException; }
+
 #endif
index 417deea..f56d50e 100644 (file)
@@ -1,8 +1,26 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _HTTP_TRANSPORT_H
 #define _HTTP_TRANSPORT_H
 
 #include "ThreadPool.H"
 #include "Transport.H"
+#include "Singleton.H"
 
 #include <string>
 
@@ -10,20 +28,18 @@ class HTTPTransport:
        public Transport
 {
 public:
-       class Factory: public Transport::Factory {
+
+       // factory class
+       class Factory: public Transport::Factory, 
+                      public Singleton<HTTPTransport::Factory>  {
        public:
-               virtual Transport *newTransport(Connection *conn) const {
-                       if(conn) 
-                               return(new HTTPTransport(conn));
-                       else 
-                               return NULL;
+               virtual Transport *newTransport() const {
+                       return(new HTTPTransport());
                }
        };
 
-       static Factory theFactory;
-
-       HTTPTransport(Connection *conn) 
-               : Transport(conn),
+       HTTPTransport() 
+               : Transport(),
                  state(NONE),
                  request(), headers(), body(NULL), pos(NULL),
                  content_length(0)
@@ -31,12 +47,9 @@ public:
 
        virtual ~HTTPTransport();
 
-
-protected:
-       // from ThreadPool::WorkDescription
-       virtual void onReady();
-       virtual void onTimeout();
-       virtual void onError();
+       virtual int receive(Connection *conn, Message* &msg);
+       virtual int send(Connection *conn, Message* msg);
+       virtual void reset();
 
 private:
        enum { NONE, 
@@ -51,6 +64,7 @@ private:
        unsigned int content_length;
 
        int parseHeader(const char *s, unsigned int len);
+       void serializeHeaders(Message *msg);
 };
 
 
index 8f495c3..ca3d378 100644 (file)
@@ -1,21 +1,39 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include "HTTPTransport.H"
 #include "Exception.H"
+#include "EventManager.H"
 
 #include <iostream>
 #include <string.h>
 
 
-HTTPTransport::Factory HTTPTransport::theFactory;
-
-
 HTTPTransport::~HTTPTransport()
 {
        if(body) free(body);
 }
 
 
-void
-HTTPTransport::onReady()
+// read what is available and parse what can be parsed
+// returns the result of read operation of the underlying connection,
+// ie. the number of bytes read or error code
+int
+HTTPTransport::receive(Connection *conn, Message* &msg)
 {
        int len;
 
@@ -29,10 +47,12 @@ HTTPTransport::onReady()
                len = conn->read(pos, sizeof(buffer) - (pos - buffer));
                if(len < 0) {
                        // error during request
-                       state = NONE;
+                       // state = NONE;
+                       return len;
                } else if(len == 0) {
                        // other side closed connection
-                       state = NONE;
+                       // state = NONE;
+                       return len;
                } else {
                        char *cr = NULL, *p = buffer, *s = buffer;
                        bool crlf_seen = false;
@@ -114,6 +134,7 @@ HTTPTransport::onReady()
                                        }
                                } else {
                                        // report error
+                                       // XXX - this may happen, do not handle using exceptions
                                        std::cout << "Wrong content length" << std::endl;
                                        throw new Exception();
                                }
@@ -131,13 +152,15 @@ HTTPTransport::onReady()
                len = conn->read(pos, content_length - (pos - body));
                if(len < 0) {
                        // error reading
-                       state = NONE;
+                       // state = NONE;
+                       return len;
                } else if(len == 0) {
                        // no more data
-                       state = NONE;
+                       // state = NONE;
+                       return len;
                } else {
                        pos += len;
-                       if(pos - body == content_length) {
+                       if(pos == content_length + body) {
                                // finished reading
                                state = NONE;
                        }
@@ -146,40 +169,104 @@ HTTPTransport::onReady()
        }
 
        if(state != NONE) 
-               ThreadPool::instance()->queueWorkRead(this);
+               msg = NULL;
        else {
-               std::cout << request << std::endl << headers << std::endl;
-               std::cout.write(body, content_length);
-               std::cout.flush();
+               // we have a new message
+               // XXX - or we have an error, must handle it
+               msg = new Message(body, content_length);
+               msg->setProperties(
        }
-
+       return len;
 }
 
 
-void 
-HTTPTransport::onTimeout()
+int
+HTTPTransport::parseHeader(const char *s, unsigned int len)
 {
+       char *p;
+
+       p = (char*)memccpy((void*)s, (void*)s, ':', len);
+       
+       if(!strncasecmp(s, "Content-Length", 14)) {
+               content_length = p ? atoi(p) : 0 ;
+       }
+       return(0);
 }
 
 
-void 
-HTTPTransport::onError()
+int
+HTTPTransport::send(Connection *conn, Message* msg)
 {
+       int len;
+       switch(state) {
+       case NONE:
+               state = IN_REQUEST;
+               request = "POST " + msg->path() + "HTTP/1.1\r\n";
+               pos = request.c_str();
+               content_length = msg->getContent(body);
+
+       case IN_REQUEST:
+               len = conn->send(pos, request.length() - pos + request.c_str());
+               if(len < 0) {
+                       return len;
+               }
+               pos += len;
+               if(request.c_str() + request.length() == pos) {
+                       state = IN_HEADERS;
+                       prepareHeaders(msg);
+                       pos = headers.c_str();
+               } else {
+                       break;
+               }
+
+       case IN_HEADERS:
+               len = conn->send(pos, headers.length() - pos + headers.c_str());
+               if(len < 0) {
+                       return len;
+               }
+               pos += len;
+               if(headers.c_str() + headers.length() == pos) {
+                       state = IN_BODY;
+                       pos = body;
+               } else {
+                       break;
+               }
+
+       case IN_BODY:
+               len = conn->send(pos, body, content_length - pos + body);
+               if(len < 0) {
+                       return len;
+               }
+               pos += len;
+               if(body + content_length == pos) {
+                       state = NONE;
+                       return 0;
+               }
+               break;
+               
+       default:
+       }
+       return len;
 }
 
 
-int
-HTTPTransport::parseHeader(const char *s, unsigned int len)
+void 
+HTTPTransport::reset()
 {
-       char *p;
-
-       std::cout << "header: ";
-       std::cout.write(s, len);
-       std::cout << std::endl;
-       std::cout.flush();
-       if(!strncasecmp(s, "Content-Length", 14)) {
-               p = (char*)memccpy((void*)s, (void*)s, ':', len);
-               content_length = p ? atoi(p) : 0 ;
+       state = NONE;
+       request.clear();
+       headers.clear();
+       if(body) {
+               free(body);
+               body = NULL;
        }
-       return(0);
+       content_length = 0;
+       pos = buffer;
+}
+       
+
+void
+HTTPTransport::serializeHeaders(Message *msg);
+{
+       for(Properties::iterator i = msg->
 }
diff --git a/org.glite.lb.logger/src-nt/InputChannel.H b/org.glite.lb.logger/src-nt/InputChannel.H
new file mode 100644 (file)
index 0000000..5f6434f
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef _INPUT_CHANNEL_H_
+#define _INPUT_CHANNEL_H_
+
+#include "ThreadPool.H"
+#include "Connection.H"
+#include "Transport.H"
+
+class InputChannel 
+       : public ThreadPool::WorkDescription {
+public:
+       
+       InputChannel(Connection *conn, Transport *trans)
+               :  ThreadPool::WorkDescription(conn->getFD()),
+                  m_connection(conn), m_transport(trans)
+               {}
+
+       void start();
+
+protected:
+       virtual void onReady();
+       virtual void onTimeout();
+       virtual void onError();
+
+private:
+       Connection *m_connection;
+       Transport  *m_transport;
+};
+
+#endif
diff --git a/org.glite.lb.logger/src-nt/InputChannel.cpp b/org.glite.lb.logger/src-nt/InputChannel.cpp
new file mode 100644 (file)
index 0000000..99d5999
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "InputChannel.H"
+#include "ThreadPool.H"
+#include "EventManager.H"
+
+extern EventManager theEventManager;
+
+void
+InputChannel::start()
+{
+       ThreadPool::instance()->queueWorkRead(this);
+}
+
+void
+InputChannel::onReady()
+{
+       Transport::Message *msg = NULL;
+       int ret = m_transport->receive(m_connection, msg);
+       if(ret <= 0) {
+               // no new data read
+       } else if(msg) {
+               // we have a new message
+               
+       } else {
+               // still need more data
+               ThreadPool::instance()->queueWorkRead(this);
+       }
+}
+
+void
+InputChannel::onTimeout()
+{
+}
+
+void
+InputChannel::onError()
+{
+}
index 2ff9420..8ebe707 100644 (file)
@@ -10,9 +10,9 @@ LINK = libtool --mode=link g++ $(LDFLAGS)
 
 THREAD_LIB = -lpthread
 
-CPPUNIT_ROOT = /afs/ruk.cuni.cz/home/michal/egee/repository/externals/cppunit/1.10.2/slc3_ia32_gcc323
-CPPUNIT_LIB = -L$(CPPUNIT_ROOT)/lib -lcppunit -ldl
-CPPUNIT_INCLUDE = -I$(CPPUNIT_ROOT)/include
+CPPUNIT_ROOT = 
+CPPUNIT_LIB = -lcppunit -ldl
+CPPUNIT_INCLUDE = 
 
 TEST_OBJS= \
        test/ThreadPoolTest.o \
@@ -21,13 +21,24 @@ TEST_OBJS= \
        test/SingletonTest.o \
        test/test_main.o
 
-plain: SocketInput.o PlainConnection.o HTTPTransport.o ThreadPool.o main.o
+OBJS = \
+       PluginManager.o \
+       SocketInput.o \
+       Connection.o \
+       PlainConnection.o \
+       Transport.o \
+       HTTPTransport.o \
+       ThreadPool.o \
+       EventManager.o \
+       InputChannel.cpp 
+
+plain: main.o $(OBJS)
        $(LINK) -o $@ $+ $(THREAD_LIB)
 
 utest: ThreadPool.o PluginManager.o EventManager.o $(TEST_OBJS)
        $(LINK) -o $@ $+ $(CPPUNIT_LIB) $(THREAD_LIB)
 
-stest: test/SingletonTest.o test/test_main.o
+stest: EventManager.o test/EventManagerTest.o test/test_main.o
        $(LINK) -o $@ $+ $(CPPUNIT_LIB) $(THREAD_LIB)
 
 $(TEST_OBJS): %.o: %.cpp
diff --git a/org.glite.lb.logger/src-nt/Message.H b/org.glite.lb.logger/src-nt/Message.H
new file mode 100644 (file)
index 0000000..9f6fcc4
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef _MESSAGE_H_
+#define _MESSAGE_H
+
+#include "Properties.H"
+#include "MessageStore.H"
+
+#include <string>
+
+class Message: public MessageStore::Storable {
+public:
+       
+       /** class that holds message state
+        *
+        */
+       class State : public MessageStore::Storable {
+       public:
+
+               /** Get size needed for storage (from Storable).
+                */
+               virtual int getStorageSize() const;
+
+               /** Save State (from Storable)
+                */
+               virtual int save(void* data, int len) const;
+
+               /** Load State (from Storable)
+                */
+               virtual int load(void* data, int len);
+       };
+
+
+       Message();
+
+       Message(void * data, unsigned int length) 
+               : m_length(length), 
+                 m_data(data)
+               {}
+
+
+       int getContent(void* &data) const
+               { data = m_data; return m_length; }
+
+       int getContentLength() const 
+               { return m_length; }
+
+       std::string getProperty(const std::string &name, std::string &val)
+               { return m_properties.getProperty(name); }
+       
+       void setProperty(const std::string &name, std::string &val) 
+               { m_properties.setProperty(name, val); }
+       
+       Properties& getProperties() 
+               { return m_properties; }
+       
+       void setProperties(Properties &)
+               {}
+
+private:
+       MessageStore::ID        m_id;
+       unsigned int            m_length;
+       void *                  m_data;
+       Properties              m_properties;
+};
+
+
+#endif
diff --git a/org.glite.lb.logger/src-nt/MessageStore.H b/org.glite.lb.logger/src-nt/MessageStore.H
new file mode 100644 (file)
index 0000000..7e47718
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef _MESSAGE_STORE_H_
+#define _MESSAGE_STORE_H_
+
+#include <pthread.h>
+
+/** Permanent storage for messages and their states.
+ */
+
+class MessageStore {
+public:
+
+       /** Base class for everything that can be stored here.
+        */
+       class Storable {
+       public:
+               /** Get size needed for object storage.
+                */
+               virtual int getStorageSize() const = 0;
+               
+               /** Save state of object into binary data.
+                */
+               virtual int save(void* data, int len) const = 0;
+
+               /** Load state of object from binary data.
+                */
+               virtual int load(void* data, int len) = 0;
+
+               virtual ~Storable() {}
+       };
+
+
+       /** Class that uniquely identifies stored content.
+        */
+       class ID: public Storable {
+       public:
+               /** Default constructor.
+                *
+                * Creates new unique ID.
+                */
+               ID();
+
+               /** Copy constructor.
+                */
+               ID(const ID& src);
+
+               /** Destructor.
+                */
+               ~ID() {};
+
+               /** Assignment operator.
+                */
+               ID& operator=(const ID& src);
+
+               /** Return the string suitable for printing.
+                */
+               std::string toString() const;
+
+               /** Comparison operator
+                */
+               int operator==(const ID& second);
+
+               /** Get size needed for storage (from Storable).
+                */
+               virtual int getStorageSize() const;
+
+               /** Save ID (from Storable)
+                */
+               virtual int save(void* data, int len) const;
+
+               /** Load ID (from Storable)
+                */
+               virtual int load(void* data, int len);
+
+       protected:
+               unsigned long long getID() {return id;}
+
+       private:
+               static pthread_mutex_t counterLock;
+               static unsigned counter;
+               unsigned long long id;
+       };
+};
+
+#endif
diff --git a/org.glite.lb.logger/src-nt/MessageStore.cpp b/org.glite.lb.logger/src-nt/MessageStore.cpp
new file mode 100644 (file)
index 0000000..e90fa44
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include <pthread.h>
+#include <sys/time.h>
+#include <sstream>
+
+#include "MessageStore.H"
+
+pthread_mutex_t MessageStore::ID::counterLock = PTHREAD_MUTEX_INITIALIZER;
+unsigned MessageStore::ID::counter = 0;
+
+MessageStore::ID::ID(){
+       time_t t;
+       time(&t);
+       pthread_mutex_lock(&counterLock);
+       counter++;
+       id = ((unsigned long long) counter << 32) + t;
+       pthread_mutex_unlock(&counterLock);
+}
+
+std::string MessageStore::ID::toString() const{
+       std::ostringstream oss;
+       oss << id;
+       return oss.str();
+}
+
index 3551286..63fba33 100644 (file)
@@ -1,23 +1,41 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _PLAIN_CONNECTION_H
 #define _PLAIN_CONNECTION_H
 
 #include "Connection.H"
-
+#include "Singleton.H"
 
 class PlainConnection: 
        public Connection
 {
 public:
-       class Factory: public Connection::Factory {
+       class Factory: public Connection::Factory, 
+                      public Singleton<PlainConnection::Factory> {
        public:
                virtual Connection *newConnection(int fd) const {
                        return new PlainConnection(fd);
                }
 
                virtual Connection *accept(int fd) const;
-       };
 
-       static Factory theFactory;
+               virtual ~Factory() {}
+       };
 
        PlainConnection(int a_fd): Connection(a_fd)
                {}
index 156ec6e..79fe74b 100644 (file)
@@ -1,12 +1,26 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include "PlainConnection.H"
 #include "ThreadPool.H"
 
 #include <sys/types.h>
 #include <sys/socket.h>
 
-PlainConnection::Factory PlainConnection::theFactory;
-
-
 PlainConnection::~PlainConnection()
 {
 }
@@ -35,4 +49,8 @@ PlainConnection::read(char *buf, unsigned int len)
 int 
 PlainConnection::write(char *buf, unsigned int len)
 {
+       int ret;
+
+       ret = ::write(fd, buf, len);
+       return ret;
 }
index 7b5aa74..98c0a52 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _PLUGIN_MANAGER_H
 #define _PLUGIN_MANAGER_H
 
@@ -21,6 +38,8 @@ public:
 
                virtual bool initialize() = 0;
                virtual bool cleanup () = 0;
+
+               virtual ~Plugin();
        };
 
        // add plugin with given name to the list of registered plugins
index df53043..e294d61 100644 (file)
@@ -1,3 +1,23 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include "PluginManager.H"
 
+PluginManager::Plugin::~Plugin() {
+}
+
 
diff --git a/org.glite.lb.logger/src-nt/Properties.H b/org.glite.lb.logger/src-nt/Properties.H
new file mode 100644 (file)
index 0000000..662b636
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#ifndef _PROPERTIES_H_
+#define _PROPERTIES_H_
+
+#include <map>
+#include <string>
+
+class Properties {
+public:
+
+       // default constructor
+       Properties() 
+               : properties()
+               {}
+
+       // accessors
+       std::string& getProperty(const std::string &key) 
+               { return properties[key]; }
+
+       void setProperty(const std::string &key, std::string &val) 
+               { properties[key] = val; }
+
+       // iterators
+       typedef std::map<std::string,std::string>::iterator  iterator;
+
+       iterator begin() 
+               { return properties.begin(); }
+
+       iterator end()
+               { return properties.end(); }
+
+       
+private:
+       std::map<std::string,std::string> properties;
+};
+
+#endif
index ce03525..1dccd07 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _SINGLETON_H
 #define _SINGLETON_H
 
index 0021816..db84f9b 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _SOCKET_INPUT_H
 #define _SOCKET_INPUT_H
 
index f38cabb..e8fc966 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <unistd.h>
 #include <sys/socket.h>
 #include <sys/types.h>
@@ -6,7 +23,8 @@
 
 #include "ThreadPool.H"
 #include "SocketInput.H"
-
+#include "InputChannel.H"
+#include "Exception.H"
 
 
 // create unix domain socket for input
@@ -21,6 +39,7 @@ SocketInput::SocketInput(const char *path,
        saddr.sun_family = AF_UNIX;
        strcpy(saddr.sun_path, path);
        fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if(fd < 0) throw new Exception;
        if(connect(fd, (struct sockaddr*)&saddr, sizeof(saddr.sun_path)) < 0) {
                if(errno == ECONNREFUSED) {
                        unlink(saddr.sun_path);
@@ -29,9 +48,10 @@ SocketInput::SocketInput(const char *path,
                // another instance running
                // throw new Exception
        }
-       bind(fd, (struct sockaddr *)&saddr, sizeof(saddr));
-       listen(fd, SOCK_QUEUE_MAX);
-       ThreadPool::instance()->setWorkAccept(this);
+       if(bind(fd, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) 
+               throw new Exception;
+       if(listen(fd, SOCK_QUEUE_MAX) < 0)
+               throw new Exception;
 }
 
 
@@ -48,8 +68,9 @@ void
 SocketInput::onReady()
 {
        Connection *conn = cFactory->accept(fd);
-       Transport  *trans = tFactory->newTransport(conn);
-       ThreadPool::instance()->queueWorkRead(trans);
+       Transport  *trans = tFactory->newTransport();
+       InputChannel *channel = new InputChannel(conn, trans);
+       channel->start();
 }
 
 
index b03d7e5..8e26927 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _THREAD_POOL_H
 #define _THREAD_POOL_H
 
@@ -23,6 +40,8 @@ public:
                        : fd(afd), event(NONE) {}
 
 
+               virtual ~WorkDescription();
+
        protected:
                enum Event { NONE, READY, TIMEOUT, ERROR } event;
                void doWork();
index 763e930..8c3aa22 100644 (file)
@@ -1,9 +1,27 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <time.h>
 #include <pthread.h>
 #include <poll.h>
 #include <sys/time.h>
 #include <time.h>
 #include <stdlib.h>
+#include <errno.h>
 
 #include <iostream>
 
@@ -69,8 +87,12 @@ ThreadPool::WaitDesc::adjustTimeout(const struct timeval &delta)
 }
 
 
+ThreadPool::WorkDescription::~WorkDescription() {
+}
+
+
 ThreadPool::ThreadPool() 
-       : work_count(0), wait_count(0), ufds_size(0), ufds(NULL), f_exit(false)
+       : f_exit(false), work_count(0), wait_count(0), ufds_size(0), ufds(NULL) 
 {
        pthread_mutex_init(&wait_queue_mutex, NULL);
        pthread_mutex_init(&work_queue_mutex, NULL);
@@ -79,6 +101,9 @@ ThreadPool::ThreadPool()
        pthread_cond_init(&wait_queue_cond_ready, NULL);
        pipe(pd);
        ufds = static_cast<struct pollfd *>(malloc(sizeof(struct pollfd)));
+       if(ufds == NULL) {
+               throw new Exception;
+       }
        ufds->fd = pd[0];
        ufds->events = POLLIN;
        ufds_size = 1;
@@ -102,6 +127,7 @@ ThreadPool::startWorkers(unsigned int n)
 
        num_workers = n;
        for(unsigned int i = 0; i < n; i++) {
+               // XXX check return 
                pthread_create(&workers[i], NULL, ThreadPool::threadMain, NULL);
        }
 }
@@ -121,11 +147,11 @@ ThreadPool::stopWorkers()
 void 
 ThreadPool::postWork(WorkDescription *work_unit)
 {
-       pthread_mutex_lock(&work_queue_mutex);
+       E_ASSERT(pthread_mutex_lock(&work_queue_mutex) >= 0);
        work_queue.push_back(work_unit);
        work_count++;
-       pthread_cond_signal(&work_queue_cond_ready);
-       pthread_mutex_unlock(&work_queue_mutex);
+       E_ASSERT(pthread_cond_signal(&work_queue_cond_ready) >= 0);
+       E_ASSERT(pthread_mutex_unlock(&work_queue_mutex) >= 0);
 }
 
 
@@ -133,12 +159,14 @@ inline
 void 
 ThreadPool::queueWork(WaitDesc *wd)
 {
-       pthread_mutex_lock(&wait_queue_mutex);
+       E_ASSERT(pthread_mutex_lock(&wait_queue_mutex) >= 0);
        wait_queue.push_back(wd);
        wait_count++;
-       pthread_cond_signal(&wait_queue_cond_ready);
-       pthread_mutex_unlock(&wait_queue_mutex);
-       write(pd[1], "1", 1);
+       E_ASSERT(pthread_cond_signal(&wait_queue_cond_ready) >= 0);
+       E_ASSERT(pthread_mutex_unlock(&wait_queue_mutex) >= 0);
+       if(write(pd[1], "1", 1) != 1) {
+               throw new Exception;
+       }
 }
 
 
@@ -211,19 +239,19 @@ ThreadPool::getWork()
        WorkDescription *work_unit = NULL;
        struct timespec timeout;
 
-       pthread_mutex_lock(&work_queue_mutex);
+       E_ASSERT(pthread_mutex_lock(&work_queue_mutex) >= 0);
        if(work_count == 0) {
                timeout.tv_sec = 1;
                timeout.tv_nsec = 0;
 //             pthread_cond_timedwait(&work_queue_cond_ready, &work_queue_mutex, &timeout);
-               pthread_cond_wait(&work_queue_cond_ready, &work_queue_mutex);
+               E_ASSERT(pthread_cond_wait(&work_queue_cond_ready, &work_queue_mutex) == 0);
        }
        if(work_count > 0) {
                work_count--;
                work_unit = work_queue.front();
                work_queue.pop_front();
        }
-       pthread_mutex_unlock(&work_queue_mutex);
+       E_ASSERT(pthread_mutex_unlock(&work_queue_mutex) >= 0);
        return work_unit;
 }
 
@@ -232,7 +260,7 @@ ThreadPool::threadCleanup(void *data)
 {
        ThreadPool *pool = ThreadPool::instance();
 
-       pthread_mutex_unlock(&(pool->work_queue_mutex));
+       E_ASSERT(pthread_mutex_unlock(&(pool->work_queue_mutex)) >= 0);
 }
 
 
@@ -263,12 +291,12 @@ ThreadPool::removeWaitDesc(std::list<WaitDesc *>::iterator &i)
        std::list<WaitDesc *>::iterator j = i;
        
        // actually this is safe even for the first element
-       pthread_mutex_lock(&wait_queue_mutex);
+       E_ASSERT(pthread_mutex_lock(&wait_queue_mutex) >= 0);
        j--;
        wait_queue.erase(i);
        wait_count--;
        i = j;
-       pthread_mutex_unlock(&wait_queue_mutex);
+       E_ASSERT(pthread_mutex_unlock(&wait_queue_mutex) >= 0);
 }
 
 
@@ -278,18 +306,18 @@ ThreadPool::prepareDescriptorArray()
        std::list<WaitDesc *>::iterator theIterator;
        struct pollfd *p;
 
-       pthread_mutex_lock(&wait_queue_mutex);
+       E_ASSERT(pthread_mutex_lock(&wait_queue_mutex) >= 0);
        if(wait_count == 0) {
-               pthread_cond_wait(&wait_queue_cond_ready, &wait_queue_mutex);
+               E_ASSERT(pthread_cond_wait(&wait_queue_cond_ready, &wait_queue_mutex) != 0);
        }
        if(wait_count == 0) {
-               pthread_mutex_unlock(&wait_queue_mutex);
+               E_ASSERT(pthread_mutex_unlock(&wait_queue_mutex) >= 0);
                return;
        }
        if(ufds_size != wait_count + 1) {
                ufds = static_cast<struct pollfd *>(realloc(ufds, (1 + wait_count) * sizeof(struct pollfd)));
                if(ufds == NULL) {
-//                     throw new Exception();
+                       throw new Exception();
                }
                ufds_size = wait_count + 1;
        }
@@ -305,7 +333,7 @@ ThreadPool::prepareDescriptorArray()
                        min_timeout = w->timeout;
                }
        }
-       pthread_mutex_unlock(&wait_queue_mutex);
+       E_ASSERT(pthread_mutex_unlock(&wait_queue_mutex) >= 0);
 }
 
 
@@ -338,9 +366,9 @@ ThreadPool::run()
                        }
 
                        // at least we have to adjust timeouts
-                       pthread_mutex_lock(&wait_queue_mutex);
+                       E_ASSERT(pthread_mutex_lock(&wait_queue_mutex) >= 0);
                        i = wait_queue.begin();
-                       pthread_mutex_unlock(&wait_queue_mutex);
+                       E_ASSERT(pthread_mutex_unlock(&wait_queue_mutex) >= 0);
                        // the wait queue mutex is unlocked inside the loop
                        // to allow handlers to add queue new
                        // WorkDescriptions - these are added at the
@@ -352,7 +380,7 @@ ThreadPool::run()
                                // check for consistency
                                if(p->fd != w->get_fd()) {
                                        // mismatch, what shall we do?
-                                       abort();
+                                       throw new Exception;
                                }
 
                                // subtract the time passed from timeout
@@ -389,12 +417,13 @@ ThreadPool::run()
                                                w->timeout.tv_usec = 0;
                                        }
                                }
-                               pthread_mutex_lock(&wait_queue_mutex);
+                               E_ASSERT(pthread_mutex_lock(&wait_queue_mutex) >= 0);
                                i++;
-                               pthread_mutex_unlock(&wait_queue_mutex);
+                               E_ASSERT(pthread_mutex_unlock(&wait_queue_mutex) >= 0);
                        }
                } else {
                        // some nasty error
+                       throw new Exception;
                }
        }
 }
index 974b8ac..09cb777 100644 (file)
@@ -1,26 +1,50 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners/ for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #ifndef _TRANSPORT_H
 #define _TRANSPORT_H
 
 #include "Connection.H"
+#include "Message.H"
 
-
-class Transport: public ThreadPool::WorkDescription {
+// Transport implements transport protocol 
+//  - reads/writes messages using Connection interface
+//  - 
+class Transport {
 public:
+
+       // 
        class Factory {
        public:
-               virtual Transport *newTransport(Connection *conn) const = 0;
+               virtual Transport *newTransport() const = 0;
+
+               virtual ~Factory() 
+                       {}
        };
 
-       Transport(Connection *a_conn) 
-               : conn(a_conn),
-                 ThreadPool::WorkDescription(a_conn ? a_conn->getFD() : -1)
+       //
+       Transport() 
                {}
 
-       virtual ~Transport() 
-               { if(conn) delete conn; }
-
-protected:
-       Connection *conn;
+       virtual ~Transport();
 
+       // 
+       virtual int receive(Connection *conn, Message* &msg) = 0;
+       virtual int send(Connection *conn, Message* msg) = 0;
+       virtual void reset() = 0;
 };
 #endif
diff --git a/org.glite.lb.logger/src-nt/Transport.cpp b/org.glite.lb.logger/src-nt/Transport.cpp
new file mode 100644 (file)
index 0000000..ec12ec4
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+#include "Transport.H"
+
+Transport::~Transport()
+{
+}
index aa7ac45..59cb0dd 100644 (file)
@@ -1,12 +1,32 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include "PluginManager.H"
 #include "ThreadPool.H"
 #include "SocketInput.H"
 #include "PlainConnection.H"
 #include "HTTPTransport.H"
+#include "EventManager.H"
 
 const int num_threads = 2;
 const char *sock_path = "/tmp/il_sock";
 
+EventManager theEventManager();
+
 int main(int argc, char *argv[])
 {
        SocketInput *input;
@@ -16,8 +36,10 @@ int main(int argc, char *argv[])
 
        // create unix socket with plain IO and HTTP transport
        input = new SocketInput(sock_path, 
-                               &PlainConnection::theFactory, 
-                               &HTTPTransport::theFactory);
+                               PlainConnection::Factory::instance(), 
+                               HTTPTransport::Factory::instance());
+       // and add the socket to pool
+       ThreadPool::instance()->setWorkAccept(input);
 
        // start worker threads
        ThreadPool::instance()->startWorkers(num_threads);
index bedaa03..1660af7 100644 (file)
@@ -1,18 +1,62 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
 
 #include "EventManager.H"
 
+class EventA : public Event {
+};
+
+class EventB : public Event {
+};
+
+class EventAA : public EventA {
+};
+
+
 class EventManagerTest: public CppUnit::TestFixture {
        CPPUNIT_TEST_SUITE(EventManagerTest);
+       CPPUNIT_TEST(handleEventTest);
        CPPUNIT_TEST_SUITE_END();
 public:
        
        void setUp() {
+               handled = false;
+               manager.registerHandler(this);
        }
 
        void tearDown() {
        }
 
+       void handleEventTest() {
+               Event *e = new EventAA();
+               manager.postEvent(e);
+               CPPUNIT_ASSERT(handled);
+       }
+
+       int handleEvent(EventA* &e) {
+               handled = true;
+               return 0;
+       }
+
+private:
+       EventManager manager;
+       bool handled;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION( EventManagerTest );
index 629bbcb..cc97d98 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
 
 #include "PluginManager.H"
index 734db17..4bc1346 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
 
 #include "Singleton.H"
index a4be75e..fd9b955 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
 #include <unistd.h>
 #include <sys/socket.h>
@@ -134,7 +151,7 @@ public:
 class ThreadPoolTest: public CppUnit::TestFixture
 {
        CPPUNIT_TEST_SUITE( ThreadPoolTest );
-//     CPPUNIT_TEST( testWorkQueue );
+       CPPUNIT_TEST( testWorkQueue );
        CPPUNIT_TEST( testPoll );
        CPPUNIT_TEST( testAccept );
        CPPUNIT_TEST_SUITE_END();
index 1ae6d6a..83c15ef 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <assert.h>
 #include <fstream>
 
diff --git a/org.glite.lb.logger/src/activemq_cpp_plugin.cpp b/org.glite.lb.logger/src/activemq_cpp_plugin.cpp
new file mode 100644 (file)
index 0000000..5e4c762
--- /dev/null
@@ -0,0 +1,400 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+/*
+ * activemq_cpp_plugin.cpp
+ *
+ *  Created on: Jan 20, 2010
+ *      Author: michal
+ */
+
+#include "interlogd.h"
+#include "glite/lb/events_parse.h"
+#include "glite/lb/context.h"
+#include "glite/lbu/escape.h"
+#include "glite/lb/jobstat.h"
+#include "glite/lb/xml_parse.h"
+
+#include <activemq/library/ActiveMQCPP.h>
+#include <cms/ConnectionFactory.h>
+#include <cms/Connection.h>
+#include <cms/Session.h>
+#include <cms/Topic.h>
+#include <cms/MessageProducer.h>
+#include <cms/ExceptionListener.h>
+#include <cms/TextMessage.h>
+#include <cms/Message.h>
+
+#include <string>
+
+#include <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+
+class OutputPlugin : public cms::ExceptionListener {
+
+public:
+
+       OutputPlugin() : session(NULL), destination(NULL), producer(NULL) {};
+
+       virtual void onException(const cms::CMSException &ex);
+
+       void connect(const std::string &topic);
+       void send(cms::Message *msg);
+       void close();
+       void cleanup();
+
+public:
+
+       cms::Session *session;
+       cms::Topic   *destination;
+       cms::MessageProducer *producer;
+
+       static cms::Connection *connection;
+       static cms::ConnectionFactory *connectionFactory;
+
+       static const char *SCHEME;
+};
+
+
+void
+OutputPlugin::onException(const cms::CMSException &ex)
+{
+       this->cleanup();
+}
+
+
+void
+OutputPlugin::connect(const std::string &topic)
+{
+       if(this->session == NULL) {
+               this->session = connection->createSession(/* TODO: ackMode */);
+               this->destination = this->session->createTopic(topic);
+               this->producer = this->session->createProducer(this->destination);
+       }
+       connection->start();
+       connection->setExceptionListener(this);
+}
+
+
+void
+OutputPlugin::send(cms::Message *msg)
+{
+       if(this->producer != NULL) {
+               this->producer->send(this->destination, msg);
+       }
+}
+
+
+void
+OutputPlugin::close()
+{
+       this->cleanup();
+       connection->stop();
+}
+
+
+void
+OutputPlugin::cleanup()
+{
+       if(this->producer != NULL) {
+               delete this->producer;
+               this->producer = NULL;
+       }
+       if(this->destination != NULL) {
+               delete this->destination;
+               this->destination = NULL;
+       }
+       if(this->session != NULL) {
+               this->session->close();
+               delete this->session;
+               this->session = NULL;
+       }
+}
+
+
+static
+void timeval2str(struct timeval *t, char **str) {
+        struct tm       *tm;
+
+        tm = gmtime(&t->tv_sec);
+        asprintf(str,"%4d-%02d-%02dT%02d:%02d:%02dZ",tm->tm_year+1900,tm->tm_mon+1,
+                tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec);
+}
+
+
+extern "C"
+int
+event_queue_connect(struct event_queue *eq)
+{
+       OutputPlugin *output;
+       std::string topicName;
+
+       if(eq->plugin_data == NULL) {
+               output = new OutputPlugin();
+               eq->plugin_data = (void*)output;
+       } else {
+               output = (OutputPlugin*)eq->plugin_data;
+       }
+
+       try {
+               output->connect(topicName);
+       } catch(cms::CMSException &e) {
+                       output->cleanup();
+                       eq->timeout = TIMEOUT;
+                       return 0;
+       }
+       eq->first_event_sent = 0;
+       return 1;
+}
+
+
+extern "C"
+int
+event_queue_send(struct event_queue *eq)
+{
+       OutputPlugin *output = (OutputPlugin*)eq->plugin_data;
+       edg_wll_Context context;
+       edg_wll_Event *notif_event;
+    edg_wll_JobStat *state_out;
+    il_octet_string_t event;
+    char *jobstat_char;
+       int ret;
+
+       assert(output != NULL);
+
+       edg_wll_InitContext(&context);
+
+       while(!event_queue_empty(eq)) {
+           struct server_msg *msg;
+           cms::TextMessage *cms_msg;
+           char *s;
+           unsigned int i;
+           std::string val;
+
+           if(event_queue_get(eq, &msg) < 0) {
+               goto err;
+           }
+
+           try {
+               if(decode_il_msg(&event, msg->msg) < 0) {
+                       set_error(IL_LBAPI, EINVAL, "event_queue_send: error parsing notification event data");
+                       goto err;
+               }
+               ret=edg_wll_ParseNotifEvent(context, event.data, &notif_event);
+               if(ret) {
+                       set_error(IL_LBAPI, ret, "event_queue_send: error parsing notification event");
+                       goto err;
+               }
+               jobstat_char = glite_lbu_UnescapeXML((const char *) notif_event->notification.jobstat);
+               if (jobstat_char == NULL) {
+                   set_error(IL_LBAPI, EINVAL, "event_queue_send: error unescaping job status");
+                   goto err;
+               }
+               if ( edg_wll_ParseJobStat(context, jobstat_char, strlen(jobstat_char), state_out)) {
+                       set_error(IL_LBAPI, EINVAL, "event_queue_send: error parsing job status");
+                       goto err;
+               }
+
+
+               cms_msg = output->session->createTextMessage();
+               /* ownerDn */
+               val.assign(state_out->owner);
+               cms_msg->setStringProperty("ownerDn", val);
+               /* voname */
+               s = edg_wll_JDLField(state_out,"VirtualOrganisation");
+               val.assign(s);
+               free(s);
+               cms_msg->setStringProperty("voname", val);
+               /* bkHost */
+               glite_jobid_getServerParts(state_out->jobId, &s, &i);
+               val.assign(s);
+               free(s);
+               cms_msg->setStringProperty("bkHost", val);
+               /* networkServer */
+               /* TODO: XXX cut out hostname */
+               val.assign(state_out->network_server);
+               cms_msg->setStringProperty("networkHost", val);
+               timeval2str(&state_out->lastUpdateTime, &s);
+               val.assign(s);
+               if(s) free(s);
+               cms_msg->setStringProperty("lastUpdateTime", val);
+               /* stateName */
+               s = edg_wll_StatToString(state_out->state);
+               val.assign(s);
+               if(s) free(s);
+               cms_msg->setStringProperty("stateName", val);
+               timeval2str(&state_out->stateEnterTime, &s);
+               val.assign(s);
+               if(s) free(s);
+               cms_msg->setStringProperty("stateStartTime", val);
+               /* condorId */
+               val.assign(state_out->condorId);
+               cms_msg->setStringProperty("condorId", val);
+               /* destSite */
+               val.assign(state_out->destination);
+               cms_msg->setStringProperty("destSite", val);
+               /* exitCode */
+               cms_msg->setIntProperty("exitCode", state_out->exit_code);
+               /* doneCode */
+               cms_msg->setIntProperty("doneCode", state_out->done_code);
+               /* statusReason */
+               val.assign(state_out->reason);
+               cms_msg->setStringProperty("statusReason", val);
+
+               free(event.data);
+               edg_wll_FreeEvent(notif_event);
+               free(notif_event);
+               edg_wll_FreeStatus(state_out);
+               free(state_out);
+               free(jobstat_char);
+           } catch(cms::CMSException &e) {
+               goto err;
+           }
+
+           try {
+               output->send(cms_msg);
+                   delete cms_msg;
+               if(event_store_commit(msg->es, msg->ev_len, queue_list_is_log(eq), msg->generation) < 0) {
+                       /* failure committing message, this is bad */
+                       goto err;
+               }
+               event_queue_remove(eq);
+               eq->first_event_sent = 1;
+           } catch(cms::CMSException &e) {
+                   delete cms_msg;
+               output->cleanup();
+               eq->timeout = TIMEOUT;
+               edg_wll_FreeContext(context);
+               return 0;
+           }
+       }
+       edg_wll_FreeContext(context);
+       return 1;
+
+err:
+       if(event.data) {
+               free(event.data);
+       }
+       if(notif_event) {
+       edg_wll_FreeEvent(notif_event);
+       free(notif_event);
+       }
+       if(jobstat_char) {
+               free(jobstat_char);
+       }
+       if(state_out) {
+               edg_wll_FreeStatus(state_out);
+               free(state_out);
+       }
+       return -1;
+}
+
+
+extern "C"
+int
+event_queue_close(struct event_queue *eq)
+{
+       OutputPlugin *output = (OutputPlugin*)eq->plugin_data;
+
+       assert(output != NULL);
+
+       try {
+               output->close();
+       } catch(cms::CMSException &e) {
+               return -1;
+       }
+       eq->first_event_sent = 0;
+       return 0;
+}
+
+
+extern "C"
+int
+plugin_init(char *config)
+{
+       char *s, *p;
+       char key[MAXPATHLEN], val[MAXPATHLEN];
+       int ret;
+       std::string brokerURI;
+
+       s = strstr(config, "[msg]");
+       if(s == NULL) {
+               set_error(IL_DL, ENOENT, "plugin_init: missing required configuration section [msg]\n");
+               return -1;
+       }
+       s = strchr(s, '\n');
+       if(s) s++;
+       while(s) {
+               if(*s == 0 || *s == '[')
+                       break;
+               p = strchr(s, '\n');
+               if(p) *p = 0;
+               ret = sscanf(s, " %s =%s", key, val);
+               if(p) *p = '\n';
+               if(ret == 2) {
+                       if(strcmp(key, "broker") == 0) {
+                               brokerURI.assign(val);
+                       }
+               }
+               s = p;
+       }
+       if(brokerURI.length() == 0) {
+               set_error(IL_DL, ENOENT, "plugin_init: broker uri not configured\n");
+               return -1;
+       }
+
+       try {
+               activemq::library::ActiveMQCPP::initializeLibrary();
+
+               OutputPlugin::connectionFactory =
+                               cms::ConnectionFactory::createCMSConnectionFactory(brokerURI);
+               OutputPlugin::connection = OutputPlugin::connectionFactory->createConnection();
+       } catch (cms::CMSException &e) {
+               try {
+                       if(OutputPlugin::connection != NULL) {
+                               delete OutputPlugin::connection;
+                               OutputPlugin::connection = NULL;
+                       }
+                       if(OutputPlugin::connectionFactory != NULL) {
+                               delete OutputPlugin::connectionFactory;
+                               OutputPlugin::connectionFactory = NULL;
+                       }
+               } catch(cms::CMSException &e) {
+               }
+               set_error(IL_DL, 0, (char*)e.what());
+               return -1;
+       }
+
+       return 0;
+}
+
+
+extern "C"
+int
+plugin_supports_scheme(const char *scheme)
+{
+       return strncmp(scheme, OutputPlugin::SCHEME, strlen(OutputPlugin::SCHEME)) == 0;
+}
+
+
+cms::Connection *OutputPlugin::connection = NULL;
+cms::ConnectionFactory *OutputPlugin::connectionFactory = NULL;
+const char *OutputPlugin::SCHEME = "x-msg:";
index fcfcfbc..726666c 100644 (file)
@@ -1,6 +1,23 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
 
-/* 
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+/*
  *   - general queue handling routines (insert, get)
  */
 
@@ -12,7 +29,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include "glite/wmsutils/jobid/cjobid.h"
+#include "glite/jobid/cjobid.h"
 
 #include "interlogd.h"
 
@@ -22,15 +39,10 @@ struct event_queue_msg {
 };
 
 struct event_queue *
-event_queue_create(char *server_name)
+event_queue_create(char *server_name, struct il_output_plugin *output)
 {
   struct event_queue *eq;
-  char *p;
-
-  p = strchr(server_name, ':');
-  
-  if(p) 
-    *p++ = 0;
+  char *p,*s, c;
 
   if((eq = malloc(sizeof(*eq))) == NULL) {
     set_error(IL_NOMEM, ENOMEM, "event_queue_create: error allocating event queue");
@@ -39,16 +51,50 @@ event_queue_create(char *server_name)
 
   memset(eq, 0, sizeof(*eq));
 
-  eq->dest_name = strdup(server_name);
+  eq->dest = strdup(server_name);
 
+  s = strstr(server_name, "://");
+  if(s == NULL) {
+         s = server_name;
+  } else {
+         s = s + 3;
+  }
+  p = strchr(s, ':');
+  if(p) {
+    *p++ = 0;
+    c = ':';
+  } else {
+         p = strchr(s, '/');
+         if(p) {
+                 *p++ = 0;
+                 c = '/';
+         }
+  }
+  eq->dest_name = strdup(s);
   if(p)
-    *(p-1) = ':';
-  
-#if defined(IL_NOTIFICATIONS)
-  eq->dest_port = atoi(p);
+    *(p-1) = c;
+
+#if defined(IL_NOTIFICATIONS) || defined(IL_WS)
+  if(p && c == ':') {
+         eq->dest_port = atoi(p);
+  } else {
+         eq->dest_port = 0; // use whatever default there is for given url scheme
+  }
 #else
-  eq->dest_port = p ? atoi(p)+1 : GLITE_WMSC_JOBID_DEFAULT_PORT+1;
+          eq->dest_port = (p && c == ':') ? atoi(p)+1 : GLITE_JOBID_DEFAULT_PORT+1;
 #endif
+
+  /* setup output functions */
+  if(output != NULL) {
+    eq->event_queue_connect = output->event_queue_connect;
+    eq->event_queue_send = output->event_queue_send;
+    eq->event_queue_close = output->event_queue_close;
+  } else {
+    eq->event_queue_connect = event_queue_connect;
+    eq->event_queue_send = event_queue_send;
+    eq->event_queue_close = event_queue_close;
+  }
+
   /* create all necessary locks */
   if(pthread_rwlock_init(&eq->update_lock, NULL)) {
     set_error(IL_SYS, errno, "event_queue_create: error creating update lock");
@@ -96,6 +142,11 @@ event_queue_free(struct event_queue *eq)
 #if defined(INTERLOGD_HANDLE_CMD) && defined(INTERLOGD_FLUSH)
   pthread_cond_destroy(&eq->flush_cond);
 #endif
+
+  if(eq->dest_name)
+         free(eq->dest_name);
+  if(eq->dest)
+         free(eq->dest);
   free(eq);
 
   return(0);
@@ -124,10 +175,15 @@ event_queue_insert(struct event_queue *eq, struct server_msg *msg)
 #if defined(INTERLOGD_EMS)
   struct event_queue_msg *tail;
 #endif
-  
+
   assert(eq != NULL);
 
-  if((el = malloc(sizeof(*el))) == NULL) 
+  if(queue_size_high > 0 && (eq->cur_len >= queue_size_high || eq->throttling)) {
+         eq->throttling = 1;
+         return 1;
+  }
+
+  if((el = malloc(sizeof(*el))) == NULL)
     return(set_error(IL_NOMEM, ENOMEM, "event_queue_insert: not enough room for queue element"));
 
   el->msg = server_msg_copy(msg);
@@ -154,9 +210,9 @@ event_queue_insert(struct event_queue *eq, struct server_msg *msg)
        eq->tail = el;
     }
     eq->tail_ems = el;
-  } else 
+  } else
 #endif
-  { 
+  {
     /* normal messages */
     if(eq->tail)
       eq->tail->prev = el;
@@ -168,7 +224,7 @@ event_queue_insert(struct event_queue *eq, struct server_msg *msg)
 #if defined(INTERLOGD_EMS)
   /* if we are inserting message between mark_prev and mark_this,
      we have to adjust mark_prev accordingly */
-  if(eq->mark_this && (el->prev == eq->mark_this)) 
+  if(eq->mark_this && (el->prev == eq->mark_this))
     eq->mark_prev = el;
 #endif
 
@@ -189,7 +245,7 @@ event_queue_get(struct event_queue *eq, struct server_msg **msg)
 
   assert(eq != NULL);
   assert(msg != NULL);
-  
+
   event_queue_lock(eq);
   el = eq->head;
 #if defined(INTERLOGD_EMS)
@@ -208,7 +264,7 @@ event_queue_get(struct event_queue *eq, struct server_msg **msg)
 }
 
 
-int 
+int
 event_queue_remove(struct event_queue *eq)
 {
   struct event_queue_msg *el;
@@ -258,12 +314,16 @@ event_queue_remove(struct event_queue *eq)
          eq->tail = NULL;
   }
 #endif
-  if(--eq->cur_len == 0) 
+  if(--eq->cur_len == 0)
          eq->times_empty++;
 
+  if(eq->cur_len <= queue_size_low) {
+         eq->throttling = 0;
+  }
+
   event_queue_unlock(eq);
   /* end of critical section */
-    
+
   server_msg_free(el->msg);
   free(el);
 
@@ -271,14 +331,15 @@ event_queue_remove(struct event_queue *eq)
 }
 
 int
-event_queue_move_events(struct event_queue *eq_s, 
-                       struct event_queue *eq_d, 
-                       int (*cmp_func)(struct server_msg *, void *), 
+event_queue_move_events(struct event_queue *eq_s,
+                       struct event_queue *eq_d,
+                       int (*cmp_func)(struct server_msg *, void *),
                        void *data)
 {
        struct event_queue_msg *p, **source_prev, **dest_tail;
 
        assert(eq_s != NULL);
+       assert(data != NULL);
 
        event_queue_lock(eq_s);
        if(eq_d) {
@@ -290,20 +351,29 @@ event_queue_move_events(struct event_queue *eq_s,
        p = *source_prev;
        eq_s->tail = NULL;
        while(p) {
-         if((*cmp_func)(p->msg, data)) {
-                       il_log(LOG_DEBUG, "  moving event at offset %d from %s:%d to %s:%d\n",
-                              p->msg->offset, eq_s->dest_name, eq_s->dest_port, 
-                              eq_d ? eq_d->dest_name : "trash", eq_d ? eq_d->dest_port : -1);
-                       il_log(LOG_DEBUG, "  current: %x, next: %x\n", p, p->prev);
+               if((*cmp_func)(p->msg, data)) {
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                        "      moving event at offset %d(%d) from %s:%d to %s:%d",
+                                        p->msg->offset, p->msg->generation, eq_s->dest_name, eq_s->dest_port,
+                          eq_d ? eq_d->dest_name : "trash", eq_d ? eq_d->dest_port : -1);
+                       /* il_log(LOG_DEBUG, "  current: %x, next: %x\n", p, p->prev); */
                        /* remove the message from the source list */
                        *source_prev = p->prev;
+                       assert(eq_s->cur_len > 0);
+                       eq_s->cur_len--;
                        if(eq_d) {
                                /* append the message at the end of destination list */
                                p->prev = NULL;
                                *dest_tail = p;
                                dest_tail = &(p->prev);
                                eq_d->tail = p;
+                               if(++eq_d->cur_len > eq_d->max_len) {
+                                       eq_d->max_len = eq_d->cur_len;
+                               }
                        } else {
+                               /* signal that the message was 'delivered' */
+                               event_store_commit(p->msg->es, p->msg->ev_len, queue_list_is_log(eq_s),
+                                                  p->msg->generation);
                                /* free the message */
                                server_msg_free(p->msg);
                                free(p);
@@ -315,6 +385,9 @@ event_queue_move_events(struct event_queue *eq_s,
                }
                p = *source_prev;
        }
+       if(eq_s->cur_len <= queue_size_low) {
+               eq_s->throttling = 0;
+       }
        if(eq_d) event_queue_unlock(eq_d);
        event_queue_unlock(eq_s);
        return(0);
index d919945..1824899 100644 (file)
@@ -1,19 +1,35 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <assert.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
 #include <stdlib.h>
+#include <time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <dirent.h>
-#ifdef HAVE_UNISTD_H
 #include <unistd.h>
-#endif
 #include <fcntl.h>
+#include <sys/param.h>
 
-#include "glite/lb/consumer.h"
 #include "glite/lb/events_parse.h"
 
 #include "interlogd.h"
@@ -29,7 +45,9 @@ static char *file_prefix = NULL;
 
 struct event_store_list {
        struct event_store *es;
-       struct event_store_list *next;
+       struct event_store_list *next;                  // LL of event_store's
+       struct event_store_list *jobid_next;       /* double LL of rotated stores - forward */
+       struct event_store_list *jobid_prev;       /* double LL of rotated stores - backward */
 };
 
 
@@ -43,6 +61,20 @@ static pthread_rwlock_t store_list_lock = PTHREAD_RWLOCK_INITIALIZER;
  */
 static
 char *
+astrcat(const char *s1, const char *s2)
+{
+       char *s = malloc(strlen(s1) + strlen(s2) + 1);
+       if(s == NULL)
+               return NULL;
+       *s = 0;
+       strcat(s, s1);
+       strcat(s, s2);
+       return s;
+}
+
+
+static
+char *
 jobid2eventfile(IL_EVENT_ID_T job_id)
 {
   char *buffer;
@@ -52,9 +84,9 @@ jobid2eventfile(IL_EVENT_ID_T job_id)
     hash = IL_EVENT_GET_UNIQUE(job_id);
     asprintf(&buffer, "%s.%s", file_prefix, hash);
     free(hash);
-  } else 
+  } else
     asprintf(&buffer, "%s.default", file_prefix);
-    
+
   return(buffer);
 }
 
@@ -70,12 +102,33 @@ jobid2controlfile(IL_EVENT_ID_T job_id)
     hash = IL_EVENT_GET_UNIQUE(job_id);
     snprintf(buffer, 256, "%s.%s.ctl", file_prefix, hash);
     free(hash);
-  } else 
+  } else
     snprintf(buffer, 256, "%s.default.ctl", file_prefix);
-    
+
   return(strdup(buffer));
 }
 
+static
+long long
+fname2index(const char *filename)
+{
+       char *p = rindex(filename, '.');
+       char *s;
+       long long       ret;
+
+       if(p == NULL)
+               return 0;
+
+       for(s = p+1; *s != 0; s++) {
+               if(*s < '0' || *s > '9') {
+                       return 0;
+               }
+       }
+
+       sscanf(p+1,"%lld",&ret);
+       return ret+1;
+}
+
 
 static
 char *
@@ -93,7 +146,7 @@ read_event_string(FILE *file)
   len = 1024;
 
   while((c=fgetc(file)) != EOF) {
-    
+
     /* we have to have free room for one byte */
     /* if(len - (p - buffer) < 1) */
     if(p - buffer >= len) {
@@ -112,7 +165,7 @@ read_event_string(FILE *file)
       *p++ = 0;
       break;
     } else
-      *p++ = (char) c; 
+      *p++ = (char) c;
   }
 
   if(c != EVENT_SEPARATOR) {
@@ -139,7 +192,8 @@ event_store_free(struct event_store *es)
   if(es->event_file_name) free(es->event_file_name);
   if(es->control_file_name) free(es->control_file_name);
   pthread_rwlock_destroy(&es->use_lock);
-  pthread_rwlock_destroy(&es->update_lock);
+  pthread_rwlock_destroy(&es->commit_lock);
+  pthread_rwlock_destroy(&es->offset_lock);
   free(es);
 
   return(0);
@@ -148,7 +202,7 @@ event_store_free(struct event_store *es)
 
 static
 struct event_store *
-event_store_create(char *job_id_s)
+event_store_create(char *job_id_s, const char *filename)
 {
   struct event_store *es;
   IL_EVENT_ID_T job_id;
@@ -161,8 +215,6 @@ event_store_create(char *job_id_s)
 
   memset(es, 0, sizeof(*es));
 
-  il_log(LOG_DEBUG, "  creating event store for id %s\n", job_id_s);
-
   job_id = NULL;
   if(strcmp(job_id_s, "default") && IL_EVENT_ID_PARSE(job_id_s, &job_id)) {
     set_error(IL_LBAPI, EDG_WLL_ERROR_PARSE_BROKEN_ULM, "event_store_create: error parsing id");
@@ -171,13 +223,20 @@ event_store_create(char *job_id_s)
   }
 
   es->job_id_s = strdup(job_id_s);
-  es->event_file_name = jobid2eventfile(job_id);
-  es->control_file_name = jobid2controlfile(job_id);
+  es->event_file_name = filename ? strdup(filename) : jobid2eventfile(job_id);
+  es->control_file_name = filename ? astrcat(filename, ".ctl") : jobid2controlfile(job_id);
+  es->rotate_index = filename ? fname2index(filename) : 0;
   IL_EVENT_ID_FREE(job_id);
 
-  if(pthread_rwlock_init(&es->update_lock, NULL)) 
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "  creating event store for id %s, filename %s, rotate index %lld",
+                  job_id_s, es->event_file_name, es->rotate_index);
+
+  if(pthread_rwlock_init(&es->commit_lock, NULL))
           abort();
-  if(pthread_rwlock_init(&es->use_lock, NULL)) 
+  if(pthread_rwlock_init(&es->offset_lock, NULL))
+          abort();
+  if(pthread_rwlock_init(&es->use_lock, NULL))
          abort();
 
   return(es);
@@ -186,24 +245,11 @@ event_store_create(char *job_id_s)
 
 static
 int
-event_store_lock_ro(struct event_store *es)
-{
-  assert(es != NULL);
-
-  if(pthread_rwlock_rdlock(&es->update_lock)) 
-    abort();
-
-  return(0);
-}
-
-
-static
-int
 event_store_lock(struct event_store *es)
 {
   assert(es != NULL);
 
-  if(pthread_rwlock_wrlock(&es->update_lock)) 
+  if(pthread_rwlock_wrlock(&es->commit_lock))
     abort();
 
   return(0);
@@ -216,7 +262,7 @@ event_store_unlock(struct event_store *es)
 {
   assert(es != NULL);
 
-  if(pthread_rwlock_unlock(&es->update_lock)) 
+  if(pthread_rwlock_unlock(&es->commit_lock))
     abort();
   return(0);
 }
@@ -262,8 +308,8 @@ event_store_write_ctl(struct event_store *es)
     return(-1);
   }
 
-  if(fprintf(ctl, "%s\n%ld\n%ld\n", 
-            es->job_id_s, 
+  if(fprintf(ctl, "%s\n%ld\n%ld\n",
+            es->job_id_s,
             es->last_committed_ls,
             es->last_committed_bs) < 0) {
     set_error(IL_SYS, errno, "event_store_write_ctl: error writing control record");
@@ -280,20 +326,23 @@ event_store_write_ctl(struct event_store *es)
 
 
 /*
- * event_store_qurantine() 
- *   - rename damaged event store file 
- *   - essentially does the same actions as cleanup, but the event store 
+ * event_store_qurantine()
+ *   - rename damaged event store file
+ *   - essentially does the same actions as cleanup, but the event store
  *     does not have to be empty
  * returns 0 on success, -1 on error
  */
 static
 int
-event_store_quarantine(struct event_store *es) 
+event_store_quarantine(struct event_store *es)
 {
+       // TODO enable cleanup of quarantined event_store struct
+       // TODO handle file rotation
+
        int num;
        char newname[MAXPATHLEN+1];
 
-       /* find available qurantine name */
+       /* find available quarantine name */
        /* we give it at most 1024 tries */
        for(num = 0; num < 1024; num++) {
                struct stat st;
@@ -306,9 +355,9 @@ event_store_quarantine(struct event_store *es)
                                break;
                        } else {
                                /* some other error with name, probably permanent */
-                               set_error(IL_SYS, errno, "event_store_qurantine: error looking for qurantine filename");
+                               set_error(IL_SYS, errno, "event_store_qurantine: error looking for quarantine filename");
                                return(-1);
-                               
+
                        }
                } else {
                        /* the filename is used already */
@@ -322,8 +371,9 @@ event_store_quarantine(struct event_store *es)
        }
 
        /* actually rename the file */
-       il_log(LOG_DEBUG, "    renaming damaged event file from %s to %s\n",
-              es->event_file_name, newname);
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                        "    renaming damaged event file from %s to %s",
+                        es->event_file_name, newname);
        if(rename(es->event_file_name, newname) < 0) {
                set_error(IL_SYS, errno, "event_store_quarantine: error renaming event file");
                return(-1);
@@ -334,11 +384,142 @@ event_store_quarantine(struct event_store *es)
        es->last_committed_bs = 0;
        es->offset = 0;
 
+       /* increase cleanup count, this will invalidate all commits from previous generation */
+       es->generation++;
+
+       return(0);
+}
+
+
+/*
+ * event_store_rotate_file()
+ * returns 0 on success, -1 on error
+ */
+static
+int
+event_store_rotate_file(struct event_store *es)
+{
+       int num;
+       time_t timestamp = time(NULL);
+       char newname[MAXPATHLEN+1];
+
+       /* do not rotate already rotated files */
+       if(es->rotate_index > 0)
+               return 0;
+
+       /* find available name */
+       /* we give it at most 256 tries */
+       for(num = 0; num < 256; num++) {
+               struct stat st;
+
+               snprintf(newname, MAXPATHLEN, "%s.%d%03d", es->event_file_name, (int) timestamp, num);
+               newname[MAXPATHLEN] = 0;
+               if(stat(newname, &st) < 0) {
+                       if(errno == ENOENT) {
+                               /* file not found */
+                               break;
+                       } else {
+                               /* some other error with name, probably permanent */
+                               set_error(IL_SYS, errno, "event_store_rotate_file: error looking for available filename");
+                               return(-1);
+
+                       }
+               } else {
+                       /* the filename is used already */
+               }
+       }
+       if(num >= 1024) {
+               /* new name not found */
+               /* XXX - is there more suitable error? */
+               set_error(IL_SYS, ENOSPC, "event_store_quarantine: exhausted number of retries looking for quarantine filename");
+               return(-1);
+       }
+
+       /* actually rename the file */
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                        "    renaming too large event file from %s to %s",
+                        es->event_file_name, newname);
+       if(rename(es->event_file_name, newname) < 0) {
+               set_error(IL_SYS, errno, "event_store_rotate_file: error renaming event file");
+               return(-1);
+       }
+
+       /* change names in event_store */
+       es->event_file_name = strdup(newname);
+       es->control_file_name = astrcat(newname, ".ctl");
+       es->rotate_index = 1000*timestamp + num + 1;
+
        return(0);
 }
 
 
 /*
+ * event_store_recover_jobid()
+ *  - recover all event stores for given jobid
+ */
+static
+int
+event_store_recover_jobid(struct event_store *es)
+{
+       // es is locked for use already
+       struct event_store_list *p = es->le;
+
+       do {
+               event_store_recover(p->es);
+               if(p != es->le ) {
+                       event_store_release(p->es);
+               }
+
+               if(pthread_rwlock_rdlock(&store_list_lock))
+                       abort();
+               p = p->jobid_next;
+               if(p != es->le) {
+                       if(pthread_rwlock_rdlock(&p->es->use_lock))
+                               abort();
+               }
+               if(pthread_rwlock_unlock(&store_list_lock))
+                       abort();
+
+
+       } while(p != es->le);
+
+       return 0;
+}
+
+#if defined(IL_NOTIFICATIONS)
+static
+int
+cmp_jobid(struct server_msg *msg, void *data)
+{
+       assert(msg != NULL);
+       assert(data != NULL);
+
+       char *job_id_s = (char*)data;
+       return strcmp(msg->job_id_s, job_id_s) == 0;
+}
+
+struct cmp_exp_data {
+       char *job_id_s;
+       time_t expires;
+};
+
+static
+int
+cmp_jobid_set_exp(struct server_msg *msg, void *data)
+{
+       struct cmp_exp_data *m = (struct cmp_exp_data *)data;
+
+       assert(msg != NULL);
+       assert(data != NULL);
+
+       if(strcmp(msg->job_id_s, m->job_id_s) == 0) {
+               msg->expires = m->expires;
+       }
+       return 0;
+}
+#endif
+
+/*
  * event_store_recover()
  *   - recover after restart or catch up when events missing in IPC
  *   - if offset > 0, read everything behind it
@@ -351,40 +532,51 @@ event_store_recover(struct event_store *es)
   struct server_msg *msg;
   char *event_s;
   int fd, ret;
-  long last;
+  int throttle;
+  long fpos, last;
   FILE *ef;
   struct flock efl;
   char err_msg[128];
   struct stat stbuf;
+#if defined(IL_NOTIFICATIONS)
+  char *last_dest = NULL;
+  time_t last_exp = 0;
+#endif
 
   assert(es != NULL);
-  
+
 #if defined(IL_NOTIFICATIONS)
-  /* destination queue has to be found for each message separately */
+  /* destination queue has to be found for each message separately, */
+  /* this is current known destination for our notification id (may be NULL!) */
+  eq_b = notifid_map_get_dest(es->job_id_s);
 #else
-  /* find bookkepping server queue */
-  eq_b = queue_list_get(es->job_id_s);
-  if(eq_b == NULL) 
-    return(-1);
-#endif
-
-#if !defined(IL_NOTIFICATIONS)
   /* get log server queue */
   eq_l = queue_list_get(NULL);
+  /* find bookkeeping server queue */
+  eq_b = queue_list_get(es->job_id_s);
+  if(eq_b == NULL)
+    return(-1);
 #endif
 
+  /* lock the event_store and offset locks */
   event_store_lock(es);
+  if(pthread_rwlock_wrlock(&es->offset_lock))
+         abort();
 
-  il_log(LOG_DEBUG, "  reading events from %s\n", es->event_file_name);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "  reading events from %s", 
+                  es->event_file_name);
 
   /* open event file */
   ef = fopen(es->event_file_name, "r");
   if(ef == NULL) {
-         snprintf(err_msg, sizeof(err_msg), 
+         snprintf(err_msg, sizeof(err_msg),
                   "event_store_recover: error opening event file %s",
                   es->event_file_name);
          set_error(IL_SYS, errno, err_msg);
          event_store_unlock(es);
+         if(pthread_rwlock_unlock(&es->offset_lock))
+                 abort();
          return(-1);
   }
 
@@ -395,11 +587,13 @@ event_store_recover(struct event_store *es)
   efl.l_start = 0;
   efl.l_len = 0;
   if(fcntl(fd, F_SETLKW, &efl) < 0) {
-         snprintf(err_msg, sizeof(err_msg), 
+         snprintf(err_msg, sizeof(err_msg),
                   "event_store_recover: error locking event file %s",
                   es->event_file_name);
          set_error(IL_SYS, errno, err_msg);
          event_store_unlock(es);
+         if(pthread_rwlock_unlock(&es->offset_lock))
+                 abort();
          fclose(ef);
          return(-1);
   }
@@ -407,19 +601,31 @@ event_store_recover(struct event_store *es)
   /* check the file modification time and size to avoid unnecessary operations */
   memset(&stbuf, 0, sizeof(stbuf));
   if(fstat(fd, &stbuf) < 0) {
-         il_log(LOG_ERR, "    could not stat event file %s: %s\n", es->event_file_name, strerror(errno));
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                          "    could not stat event file %s: %s", 
+                          es->event_file_name, strerror(errno));
          fclose(ef);
          event_store_unlock(es);
+         if(pthread_rwlock_unlock(&es->offset_lock))
+                 abort();
          return -1;
   } else {
          if((es->offset == stbuf.st_size) && (es->last_modified == stbuf.st_mtime)) {
-                 il_log(LOG_DEBUG, "  event file not modified since last visit, skipping\n");
+                 glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                  "  event file not modified since last visit, skipping");
                  fclose(ef);
                  event_store_unlock(es);
+                 if(pthread_rwlock_unlock(&es->offset_lock))
+                         abort();
                  return(0);
          }
   }
 
+  /* check the file size, rename it if it is bigger than max_store_size */
+  if(max_store_size > 0 && stbuf.st_size > max_store_size) {
+         event_store_rotate_file(es);
+  }
+
   while(1) { /* try, try, try */
 
          /* get the position in file to be sought */
@@ -427,7 +633,7 @@ event_store_recover(struct event_store *es)
                  last = es->offset;
          else {
 #if !defined(IL_NOTIFICATIONS)
-                 if(eq_b == eq_l) 
+                 if(eq_b == eq_l)
                          last = es->last_committed_ls;
                  else
 #endif
@@ -440,9 +646,12 @@ event_store_recover(struct event_store *es)
                          last = es->last_committed_bs;
          }
 
-         il_log(LOG_DEBUG, "    setting starting file position to  %ld\n", last);
-         il_log(LOG_DEBUG, "    bytes sent to logging server: %d\n", es->last_committed_ls);
-         il_log(LOG_DEBUG, "    bytes sent to bookkeeping server: %d\n", es->last_committed_bs);
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                          "    setting starting file position to  %ld", last);
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                          "    bytes sent to logging server: %d", es->last_committed_ls);
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                          "    bytes sent to bookkeeping server: %d", es->last_committed_bs);
 
          if(last > 0) {
                  int c;
@@ -454,15 +663,17 @@ event_store_recover(struct event_store *es)
                          set_error(IL_SYS, errno, "event_store_recover: error setting position for read");
                          event_store_unlock(es);
                          fclose(ef);
+                         if(pthread_rwlock_unlock(&es->offset_lock))
+                                 abort();
                          return(-1);
                  }
                  /* the last enqueued event MUST end with EVENT_SEPARATOR,
                     even if the offset points at EOF */
                  if((c=fgetc(ef)) != EVENT_SEPARATOR) {
                          /* Houston, we have got a problem */
-                         il_log(LOG_WARNING, 
-                                "    file position %ld does not point at the beginning of event string, backing off!\n",
-                                last);
+                         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN,
+                                          "    file position %ld does not point at the beginning of event string, backing off!",
+                                          last);
                          /* now, where were we? */
                          if(es->offset) {
                                  /* next try will be with
@@ -484,19 +695,39 @@ event_store_recover(struct event_store *es)
                          set_error(IL_SYS, errno, "event_store_recover: error setting position for read");
                          event_store_unlock(es);
                          fclose(ef);
+                         if(pthread_rwlock_unlock(&es->offset_lock))
+                                 abort();
                          return(-1);
                  }
                  break;
          }
   }
 
+  /* now we have:
+   *   - event file opened at position 'last'
+   *   - offset and last_committed_* potentially reset to zero
+   */
+
+  /* release lock on commits, offset remains locked;
+   * other threads are allowed to send/remove events, but not insert
+   */
+  event_store_unlock(es);
+
   /* enqueue all remaining events */
   ret = 1;
   msg = NULL;
+  throttle = 0;
+  fpos = last;
   while((event_s=read_event_string(ef)) != NULL) {
-       
-    /* last holds the starting position of event_s in file */
-    il_log(LOG_DEBUG, "    reading event at %ld\n", last);
+    long last_ls, last_bs;
+    int r;
+
+    /* fpos holds the starting position of event_s in file */
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "    reading event at %ld", fpos);
+
+    last_ls = es->last_committed_ls;
+    last_bs = es->last_committed_bs;
 
     /* break from now on means there was some error */
     ret = -1;
@@ -511,62 +742,155 @@ event_store_recover(struct event_store *es)
            free(event_s);
     }
     if(msg == NULL) {
-           il_log(LOG_ALERT, "    event file corrupted! I will try to move it to quarantine (ie. rename it).\n");
+           glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                            "    event file corrupted! I will try to move it to quarantine (ie. rename it).");
            /* actually do not bother if quarantine succeeded or not - we could not do more */
            event_store_quarantine(es);
            fclose(ef);
-           event_store_unlock(es);
+           if(pthread_rwlock_unlock(&es->offset_lock))
+                   abort();
            return(-1);
     }
     msg->es = es;
+    msg->generation = es->generation;
+
+#ifdef IL_NOTIFICATIONS
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "message dest %s, last dest %s, known dest %s",
+                    msg->dest, last_dest, eq_b ? eq_b->dest : "none");
+    /* check message destination */
+    if(msg->dest == NULL) {
+            /* the message does not have destination itself, use destination cached for notification id */
+           if(eq_b == NULL) {
+                   /* no destination is known for notification id, commit it immediately */
+                   glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                    "    message has no known destination, will not be sent");
+                   event_store_commit(es, msg->ev_len, 0, msg->generation);
+           }
+    } else {
+        /* check if we know destination for notification id */
+        if(eq_b == NULL) {
+               eq_b = queue_list_get(msg->dest);
+               if(notifid_map_set_dest(es->job_id_s, eq_b) < 0) {
+                       break;
+               }
+        }
+       /* remember last message destination */
+       if(last_dest == NULL || strcmp(msg->dest, last_dest) != 0) {
+               /* destination changed */
+               if(last_dest) {
+                       free(last_dest);
+               }
+               last_dest = strdup(msg->dest);
+       }
+    }
 
+    /* check message expiration */
+    if(last_exp == 0 || last_exp != msg->expires) {
+       last_exp = msg->expires;
+    }
+#else
     /* first enqueue to the LS */
-    if(!bs_only && (last >= es->last_committed_ls)) {
-      
-      il_log(LOG_DEBUG, "      queueing event at %ld to logging server\n", last);
+    if(!bs_only && (last >= last_ls)) {
 
-#if !defined(IL_NOTIFICATIONS)
-      if(enqueue_msg(eq_l, msg) < 0)
-       break;
-#endif
-      }
+           glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                            "      queuing event at %ld to logging server", last);
 
-#ifdef IL_NOTIFICATIONS
-    eq_b = queue_list_get(msg->dest);
+      /* TODO: throttling for the log server queue? */
+      if(enqueue_msg(eq_l, msg) < 0) {
+         break;
+      }
+    }
 #endif
 
-    /* now enqueue to the BS, if neccessary */
-    if((eq_b != eq_l) && 
-       (last >= es->last_committed_bs)) {
-      
-      il_log(LOG_DEBUG, "      queueing event at %ld to bookkeeping server\n", last);
-      
-      if(enqueue_msg(eq_b, msg) < 0)
-       break;
+    /* now enqueue to the BS, if necessary */
+    if(!throttle && (eq_b != eq_l) && (last >= last_bs)) {
+
+      if((r=enqueue_msg(eq_b, msg)) < 0) {
+         break;
+      } else if(r > 0) {
+             throttle = 1;
+             glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                              "      queue max length limit reached, event at %ld throttled", fpos);
+      } else {
+             glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                              "      queuing event at %ld to bookkeeping server", last);
+      }
     }
     server_msg_free(msg);
     msg = NULL;
 
+    fpos = ftell(ef);
     /* now last is also the offset behind the last successfully queued event */
-    last = ftell(ef);
+    if(!throttle) {
+           last = fpos;
+    }
 
     /* ret == 0 means EOF or incomplete event found */
     ret = 0;
 
   } /* while */
 
-  /* due to this little assignment we had to lock the event_store for writing */
+#if defined(IL_NOTIFICATIONS)
+  /* check if we have to move events to new destination */
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "    last destination %s, last known destination %s", 
+                  last_dest, eq_b ? eq_b->dest : "none");
+  if(last_dest && strcmp(last_dest, eq_b->dest)) {
+         struct event_queue *eq_dest = queue_list_get(last_dest);
+
+         /* set new destination */
+         if(notifid_map_set_dest(es->job_id_s, eq_dest) < 0) {
+                 ret = -1;
+         } else {
+
+                 /* move all events with this notif_id from eq_b to eq_dest */
+                 event_queue_move_events(eq_b, eq_dest, cmp_jobid, es->job_id_s);
+                 eq_b = eq_dest;
+                 glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                                  "    all messages for notif id %s are now destined to %s",
+                        es->job_id_s, eq_b->dest);
+                 if(event_queue_create_thread(eq_b) < 0) {
+                         ret = -1;
+                 } else {
+                         event_queue_cond_lock(eq_b);
+                         event_queue_signal(eq_b);
+                         event_queue_cond_unlock(eq_b);
+                 }
+         }
+  }
+  if(last_dest) {
+         free(last_dest);
+         last_dest = NULL;
+  }
+
+  /* if the expiration changed, set new one */
+  if(eq_b && last_exp != notifid_map_get_expiration(es->job_id_s)) {
+         struct cmp_exp_data data;
+
+         notifid_map_set_expiration(es->job_id_s, last_exp);
+         /* set expiration for all events with this notif id */
+         data.job_id_s = es->job_id_s;
+         data.expires = last_exp;
+         event_queue_move_events(eq_b, NULL, cmp_jobid_set_exp, &data);
+  }
+#endif
+
   es->offset = last;
   es->last_modified = stbuf.st_mtime;
-  il_log(LOG_DEBUG, "  event store offset set to %ld\n", last);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "  event store offset set to %ld", last);
 
-  if(msg) 
+  if(msg)
     server_msg_free(msg);
 
   fclose(ef);
-  il_log(LOG_DEBUG, "  finished reading events with %d\n", ret);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "  finished reading events with %d", ret);
+
+  if(pthread_rwlock_unlock(&es->offset_lock))
+         abort();
 
-  event_store_unlock(es);
   return(ret);
 }
 
@@ -590,51 +914,9 @@ event_store_sync(struct event_store *es, long offset)
    * event will be read from file, socket now serves only to notify
    * about possible event file change.
    */
-  ret = event_store_recover(es);
+  ret = event_store_recover_jobid(es);
   ret = (ret < 0) ? ret : 0;
   return(ret);
-
-#if 0
-  event_store_lock_ro(es);
-  if(es->offset == offset) 
-    /* we are up to date */
-    ret = 1;
-  else if(es->offset > offset)
-    /* we have already seen this event */
-    ret = 0;
-  else {
-    /* es->offset < offset, i.e. we have missed some events */
-    event_store_unlock(es);
-    ret = event_store_recover(es);
-    /* XXX possible room for intervention by another thread - is there 
-     * any other thread messing with us? 
-     * 1) After recover() es->offset is set at the end of file. 
-     * 2) es->offset is set only by recover() and next().
-     * 3) Additional recover can not do much harm.
-     * 4) And next() is only called by the same thread as sync().
-     * 5) use_lock is in place, so no cleanup possible
-      * => no one is messing with us right now */
-    event_store_lock_ro(es);
-    if(ret < 0)
-      ret = -1;
-    else 
-           if(es->offset <= offset) {
-                   /* Apparently there is something wrong - we are receiving an event
-                    * which is beyond the end of file. Someone must have removed the file
-                    * when we were not looking. The question is - what should we do with the event?
-                    * We have to send it, as this is the only one occasion when we see it.
-                    * However, we must not allow the es->offset to be set using this event,
-                    * as it would point after the end of file. Sort this out in event_store_next().
-                    */
-                   ret = 1;
-           } else if(es->offset > offset) {
-                   /* we have seen at least this event */
-                   ret = 0;
-           }
-  }
-  event_store_unlock(es);
-  return(ret);
-#endif
 }
 
 
@@ -642,39 +924,32 @@ int
 event_store_next(struct event_store *es, long offset, int len)
 {
   assert(es != NULL);
-  
-  /* Commented out due to the fact that offset as received on socket
+
+  /* offset as received on socket
    * has little to do with real event file at the moment. es->offset
    * handling is left solely to the event_store_recover().
    */
-   
-#if 0
-  event_store_lock(es);
-  /* Whoa, be careful now. The es->offset points right after the last enqueued event,
-   * but it may not be the offset of the event WE have just enqueued, because:!    
-   *  1) someone could have removed the event file behind our back
-   *  2) the file could have been recover()ed and more events read
-   * In either case the offset should not be moved.
-   */
-  if(es->offset == offset) {
-         es->offset += len;
-  }
-  event_store_unlock(es);
-#endif
 
   return(0);
 }
 
 
-/* 
+/*
  * event_store_commit()
  *
  */
 int
-event_store_commit(struct event_store *es, int len, int ls)
+event_store_commit(struct event_store *es, int len, int ls, int generation)
 {
   assert(es != NULL);
 
+  /* do not move counters if event store with this message was cleaned up
+   * (this can happen only when moving to quarantine)
+   */
+  /* XXX - assume int access is atomic */
+  if(generation != es->generation)
+         return 0;
+
   event_store_lock(es);
 
   if(ls)
@@ -704,7 +979,7 @@ event_store_commit(struct event_store *es, int len, int ls)
  * Q: How do we know that we can safely remove the files?
  * A: When all events from file have been committed both by LS and BS.
  */
-static 
+static
 int
 event_store_clean(struct event_store *es)
 {
@@ -717,20 +992,28 @@ event_store_clean(struct event_store *es)
 
   /* prevent sender threads from updating */
   event_store_lock(es);
-  
-  il_log(LOG_DEBUG, "  trying to cleanup event store %s\n", es->job_id_s);
-  il_log(LOG_DEBUG, "    bytes sent to logging server: %d\n", es->last_committed_ls);
-  il_log(LOG_DEBUG, "    bytes sent to bookkeeping server: %d\n", es->last_committed_bs);
+
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "  trying to cleanup event store %s", es->job_id_s);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "    bytes sent to logging server: %d", es->last_committed_ls);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "    bytes sent to bookkeeping server: %d", es->last_committed_bs);
 
   /* preliminary check to avoid opening event file */
   /* if the positions differ, some events still have to be sent */
   if(es->last_committed_ls != es->last_committed_bs) {
     event_store_unlock(es);
-    il_log(LOG_DEBUG, "  not all events sent, cleanup aborted\n");
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "  not all events sent, cleanup aborted");
     return(0);
   }
 
-  /* the file can only be removed when all the events were succesfully sent 
+  if((fd = pthread_rwlock_wrlock(&es->offset_lock)) != 0) {
+         abort();
+  }
+
+  /* the file can only be removed when all the events were succesfully sent
      (ie. committed both by LS and BS */
   /* That also implies that the event queues are 'empty' at the moment. */
   ef = fopen(es->event_file_name, "r+");
@@ -738,21 +1021,27 @@ event_store_clean(struct event_store *es)
     /* if we can not open the event store, it is an error and the struct should be removed */
     /* XXX - is it true? */
     event_store_unlock(es);
-    il_log(LOG_ERR,  "  event_store_clean: error opening event file: %s\n", strerror(errno));
+    if(pthread_rwlock_unlock(&es->offset_lock))
+           abort();
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR,  
+                    "  event_store_clean: error opening event file: %s", strerror(errno));
     return(1);
   }
-  
+
   fd = fileno(ef);
-  
+
   /* prevent local-logger from writing into event file */
   efl.l_type = F_WRLCK;
   efl.l_whence = SEEK_SET;
   efl.l_start = 0;
   efl.l_len = 0;
   if(fcntl(fd, F_SETLK, &efl) < 0) {
-    il_log(LOG_DEBUG, "    could not lock event file, cleanup aborted\n");
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                         "    could not lock event file, cleanup aborted");
     fclose(ef);
     event_store_unlock(es);
+    if(pthread_rwlock_unlock(&es->offset_lock))
+           abort();
     if(errno != EACCES &&
        errno != EAGAIN) {
       set_error(IL_SYS, errno, "event_store_clean: error locking event file");
@@ -760,47 +1049,63 @@ event_store_clean(struct event_store *es)
     }
     return(0);
   }
-  
+
   /* now the file should not contain partially written event, so it is safe
      to get offset behind last event by seeking the end of file */
   if(fseek(ef, 0, SEEK_END) < 0) {
     set_error(IL_SYS, errno, "event_store_clean: error seeking the end of file");
     event_store_unlock(es);
+    if(pthread_rwlock_unlock(&es->offset_lock))
+           abort();
     fclose(ef);
     return(-1);
   }
-  
+
   last = ftell(ef);
-  il_log(LOG_DEBUG, "    total bytes in file: %d\n", last);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "    total bytes in file: %d", last);
 
   if(es->last_committed_ls < last) {
     fclose(ef);
     event_store_unlock(es);
-    il_log(LOG_DEBUG, "    events still waiting in queue, cleanup aborted\n");
+    if(pthread_rwlock_unlock(&es->offset_lock))
+           abort();
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "    events still waiting in queue, cleanup aborted");
     return(0);
   } else if( es->last_committed_ls > last) {
-         il_log(LOG_WARNING, "  warning: event file seems to shrink!\n");
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                          "  warning: event file seems to shrink!");
          /* XXX - in that case we can not continue because there may be
             some undelivered events referring to that event store */
          fclose(ef);
          event_store_unlock(es);
+         if(pthread_rwlock_unlock(&es->offset_lock))
+                 abort();
          return(0);
   }
-  
+
   /* now we are sure that all events were sent and the event queues are empty */
-  il_log(LOG_INFO, "    removing event file %s\n", es->event_file_name);
-  
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                  "    removing event file %s", es->event_file_name);
+
   /* remove the event file */
   unlink(es->event_file_name);
   unlink(es->control_file_name);
-  
+
   /* clear the counters */
   es->last_committed_ls = 0;
   es->last_committed_bs = 0;
   es->offset = 0;
 
+  /* increasing the generation count is rather pointless here, because there
+     are no messages waiting in the queue that would be invalidated */
+  /* es->generation++ */
+
   /* unlock the event_store even if it is going to be removed */
   event_store_unlock(es);
+  if(pthread_rwlock_unlock(&es->offset_lock))
+         abort();
 
   /* close the event file (that unlocks it as well) */
   fclose(ef);
@@ -815,9 +1120,9 @@ event_store_clean(struct event_store *es)
  * --------------------------------
  */
 struct event_store *
-event_store_find(char *job_id_s)
+event_store_find(char *job_id_s, const char *filename)
 {
-  struct event_store_list *q, *p;
+  struct event_store_list *q, *p, *d;
   struct event_store *es;
 
   if(pthread_rwlock_wrlock(&store_list_lock)) {
@@ -825,27 +1130,31 @@ event_store_find(char *job_id_s)
   }
 
   es = NULL;
-  
-  q = NULL;
+
+  d = NULL;
   p = store_list;
-  
+
   while(p) {
     if(strcmp(p->es->job_id_s, job_id_s) == 0) {
-      es = p->es;
-      if(pthread_rwlock_rdlock(&es->use_lock))
-             abort();
-      if(pthread_rwlock_unlock(&store_list_lock)) 
-             abort();
-      return(es);
+           es = p->es;
+           d = p;
+       // if filename was given, compare it as well
+           if((filename == NULL && p->es->rotate_index == 0) ||
+              (filename != NULL && strcmp(p->es->event_file_name, filename) == 0)) {
+               if(pthread_rwlock_rdlock(&es->use_lock))
+                       abort();
+               if(pthread_rwlock_unlock(&store_list_lock))
+                       abort();
+               return(es);
+       }
     }
-
-    q = p;
     p = p->next;
   }
 
-  es = event_store_create(job_id_s);
+  // event store for given jobid and filename was not found, create one
+  es = event_store_create(job_id_s, filename);
   if(es == NULL) {
-         if(pthread_rwlock_unlock(&store_list_lock)) 
+         if(pthread_rwlock_unlock(&store_list_lock))
                  abort();
          return(NULL);
   }
@@ -853,20 +1162,66 @@ event_store_find(char *job_id_s)
   p = malloc(sizeof(*p));
   if(p == NULL) {
     set_error(IL_NOMEM, ENOMEM, "event_store_find: no room for new event store");
-      if(pthread_rwlock_unlock(&store_list_lock)) 
+      if(pthread_rwlock_unlock(&store_list_lock))
              abort();
     return(NULL);
   }
-  
-  p->next = store_list;
-  store_list = p;
-    
   p->es = es;
+  p->jobid_next = p;
+  p->jobid_prev = p;
+  es->le = p;
+
+  if(filename != NULL && d != NULL) {
+         // there is another event store for this jobid;
+         //    d points to the last event store for this jobid in LL
+         // find proper place to insert new event store
+         if(p->es->rotate_index == 0) {
+                 // insert behind d in LL
+                 p->next = d->next;
+                 d->next = p;
+                 // insert behind d in jobid LL
+                 p->jobid_next = d->jobid_next;
+                 p->jobid_prev = d;
+                 d->jobid_next->jobid_prev = p;
+                 d->jobid_next = p;
+         } else {
+                 struct event_store_list *r;
+                 q = NULL;
+                 for(r = d->jobid_next; r != d->jobid_next; r = r->jobid_next) {
+                         if(p->es->rotate_index < r->es->rotate_index)
+                                 break;
+                         if(r->es->rotate_index > 0)
+                                 q = r;
+                 }
+                 // q has the last lesser non-zero index than p
+                 if(q == NULL) {
+                         p->next = store_list;
+                         store_list = p;
+                         // insert behind d
+                         p->jobid_next = d->jobid_next;
+                         p->jobid_prev = d;
+                         d->jobid_next->jobid_prev = p;
+                         d->jobid_next = p;
+                 } else {
+                         p->next = q->next;
+                         q->next = p;
+                         // insert behind q
+                         p->jobid_next = q->jobid_next;
+                         p->jobid_prev = q;
+                         q->jobid_next->jobid_prev = p;
+                         q->jobid_next = p;
+                 }
+         }
+  } else {
+         // insert at the beginning
+         p->next = store_list;
+         store_list = p;
+  }
 
   if(pthread_rwlock_rdlock(&es->use_lock))
          abort();
 
-  if(pthread_rwlock_unlock(&store_list_lock)) 
+  if(pthread_rwlock_unlock(&store_list_lock))
          abort();
 
   return(es);
@@ -880,7 +1235,8 @@ event_store_release(struct event_store *es)
 
        if(pthread_rwlock_unlock(&es->use_lock))
                abort();
-       il_log(LOG_DEBUG, "  released lock on %s\n", es->job_id_s);
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                        "  released lock on %s (%s)", es->job_id_s, es->event_file_name);
        return(0);
 }
 
@@ -896,15 +1252,13 @@ event_store_from_file(char *filename)
 #if defined(IL_NOTIFICATIONS)
        edg_wll_Event *notif_event;
        edg_wll_Context context;
-       char *dest_name = NULL;
-
-       edg_wll_InitContext(&context);
 #endif
-       
-       il_log(LOG_INFO, "  attaching to event file: %s\n", filename);
-       
+
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                        "  attaching to event file: %s", filename);
+
        if(strstr(filename, "quarantine") != NULL) {
-               il_log(LOG_INFO, "  file name belongs to quarantine, not touching that.\n");
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, "  file name belongs to quarantine, not touching that.");
                return(0);
        }
 
@@ -915,17 +1269,20 @@ event_store_from_file(char *filename)
        }
        event_s = read_event_string(event_file);
        fclose(event_file);
-       if(event_s == NULL) 
+       if(event_s == NULL)
                return(0);
-       
+
 #if defined(IL_NOTIFICATIONS)
-       if((ret=edg_wll_ParseNotifEvent(context, event_s, &notif_event))) {
+       edg_wll_InitContext(&context);
+       ret=edg_wll_ParseNotifEvent(context, event_s, &notif_event);
+       edg_wll_FreeContext(context);
+       if(ret) {
                set_error(IL_LBAPI, ret, "event_store_from_file: could not parse event");
                ret = -1;
                goto out;
        }
        if(notif_event->notification.notifId == NULL) {
-               set_error(IL_LBAPI, EDG_WLL_ERROR_PARSE_BROKEN_ULM, 
+               set_error(IL_LBAPI, EDG_WLL_ERROR_PARSE_BROKEN_ULM,
                          "event_store_from_file: parse error - no notif id");
                ret = -1;
                goto out;
@@ -935,23 +1292,28 @@ event_store_from_file(char *filename)
                ret = -1;
                goto out;
        }
-       if(notif_event->notification.dest_host && 
+       /*  XXX: what was that good for?
+       if(notif_event->notification.dest_host &&
           (strlen(notif_event->notification.dest_host) > 0)) {
+               char *dest_name = NULL;
                asprintf(&dest_name, "%s:%d", notif_event->notification.dest_host, notif_event->notification.dest_port);
        }
-       
+       */
+
 #else
        job_id_s = edg_wll_GetJobId(event_s);
 #endif
-       il_log(LOG_DEBUG, "  event id: '%s'\n", job_id_s);
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                        "  event id: '%s'", job_id_s);
        if(job_id_s == NULL) {
-               il_log(LOG_NOTICE, "  skipping file, could not parse event\n");
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                "  skipping file, could not parse event");
                ret = 0;
                goto out;
        }
-       
-       es=event_store_find(job_id_s);
-       
+
+       es = event_store_find(job_id_s, filename);
+
        if(es == NULL) {
                ret = -1;
                goto out;
@@ -961,9 +1323,9 @@ event_store_from_file(char *filename)
           (es->last_committed_bs == 0) &&
           (es->offset == 0)) {
                ret = event_store_read_ctl(es);
-       } else 
+       } else
                ret = 0;
-       
+
        event_store_release(es);
 
 out:
@@ -973,7 +1335,7 @@ out:
                free(notif_event);
        }
 #endif
-       if(event_s) free(event_s); 
+       if(event_s) free(event_s);
        if(job_id_s) free(job_id_s);
        return(ret);
 }
@@ -1015,12 +1377,12 @@ event_store_init(char *prefix)
       set_error(IL_SYS, errno, "event_store_init: error opening event directory");
       return(-1);
     }
-    
+
     while((entry=readdir(event_dir))) {
       char *s;
 
       /* skip all files that do not match prefix */
-      if(strncmp(entry->d_name, p, len) != 0) 
+      if(strncmp(entry->d_name, p, len) != 0)
        continue;
 
       /* skip all control files */
@@ -1058,12 +1420,12 @@ event_store_init(char *prefix)
       set_error(IL_SYS, errno, "event_store_init: error opening event directory");
       return(-1);
     }
-    
+
     while((entry=readdir(event_dir))) {
       char *s;
 
       /* skip all files that do not match prefix */
-      if(strncmp(entry->d_name, p, len) != 0) 
+      if(strncmp(entry->d_name, p, len) != 0)
        continue;
 
       /* find all control files */
@@ -1093,10 +1455,12 @@ event_store_init(char *prefix)
              } else {
                      /* could not stat file, remove ctl */
                      strcat(ef, s);
-                     il_log(LOG_DEBUG, "  removing stale file %s\n", ef);
-                     if(unlink(ef)) 
-                             il_log(LOG_ERR, "  could not remove file %s: %s\n", ef, strerror(errno));
-                     
+                     glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                      "  removing stale file %s", ef);
+                     if(unlink(ef))
+                             glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                              "  could not remove file %s: %s\n", ef, strerror(errno));
+
              }
              free(ef);
 
@@ -1116,7 +1480,7 @@ event_store_recover_all()
   struct event_store_list *sl;
 
 
-  if(pthread_rwlock_rdlock(&store_list_lock)) 
+  if(pthread_rwlock_rdlock(&store_list_lock))
          abort();
 
   /* recover all event stores */
@@ -1126,20 +1490,22 @@ event_store_recover_all()
          /* recover this event store */
          /* no need to lock use_lock in event_store, the store_list_lock is in place */
          if(event_store_recover(sl->es) < 0) {
-                 il_log(LOG_ERR, "  error recovering event store %s:\n    %s\n", sl->es->event_file_name, error_get_msg());
+                 glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                  "  error recovering event store %s:  %s", 
+                                  sl->es->event_file_name, error_get_msg());
                  clear_error();
          }
          sl = sl->next;
   }
-  
-  if(pthread_rwlock_unlock(&store_list_lock)) 
+
+  if(pthread_rwlock_unlock(&store_list_lock))
          abort();
 
   return(0);
 }
 
 
-#if 0 
+#if 0
 int
 event_store_remove(struct event_store *es)
 {
@@ -1149,9 +1515,10 @@ event_store_remove(struct event_store *es)
 
   switch(event_store_clean(es)) {
   case 0:
-    il_log(LOG_DEBUG, "  event store not removed, still used\n");
-    return(0);
-    
+         glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                          "  event store not removed, still used");
+         return(0);
+
   case 1:
     if(pthread_rwlock_wrlock(&store_list_lock) < 0) {
       set_error(IL_SYS, errno, "  event_store_remove: error locking event store list");
@@ -1195,7 +1562,7 @@ event_store_cleanup()
 
   /* try to remove event files */
 
-  if(pthread_rwlock_wrlock(&store_list_lock)) 
+  if(pthread_rwlock_wrlock(&store_list_lock))
          abort();
 
   sl = store_list;
@@ -1205,11 +1572,12 @@ event_store_cleanup()
          int ret;
 
          slnext = sl->next;
-         
+
          /* one event store at time */
          ret = pthread_rwlock_trywrlock(&sl->es->use_lock);
          if(ret == EBUSY) {
-                 il_log(LOG_DEBUG, "  event_store %s is in use by another thread\n", 
+                 glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                                  "  event_store %s is in use by another thread",
                         sl->es->job_id_s);
                  sl = slnext;
                  continue;
@@ -1217,33 +1585,39 @@ event_store_cleanup()
            abort();
 
          switch(event_store_clean(sl->es)) {
-                 
+
          case 1:
-                 /* remove this event store */
+                 /* remove this event store from LL */
                  (*prev) = slnext;
+                 /* remove this event store from jobid's LL */
+                 if(sl->jobid_next != sl) {
+                         sl->jobid_prev->jobid_next = sl->jobid_next;
+                         sl->jobid_next->jobid_prev = sl->jobid_prev;
+                 }
                  event_store_free(sl->es);
                  free(sl);
                  break;
-                 
+
          case -1:
-                 il_log(LOG_ERR, "  error removing event store %s (file %s):\n    %s\n", 
-                        sl->es->job_id_s, sl->es->event_file_name, error_get_msg());
+                 glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                  "  error removing event store %s (file %s):   %s",
+                                  sl->es->job_id_s, sl->es->event_file_name, error_get_msg());
                  /* event_store_release(sl->es); */
                  clear_error();
                  /* go on to the next */
-                 
+
          default:
                  event_store_release(sl->es);
                  prev = &(sl->next);
                  break;
          }
-         
+
          sl = slnext;
   }
-  
-  if(pthread_rwlock_unlock(&store_list_lock)) 
+
+  if(pthread_rwlock_unlock(&store_list_lock))
          abort();
-  
+
   return(0);
 }
 
diff --git a/org.glite.lb.logger/src/event_store_http.c b/org.glite.lb.logger/src/event_store_http.c
new file mode 100644 (file)
index 0000000..4f09b26
--- /dev/null
@@ -0,0 +1,1130 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/param.h>
+
+#include "glite/lb/events_parse.h"
+
+#include "interlogd.h"
+
+#ifdef __GNUC__
+#define UNUSED_VAR __attribute__((unused))
+#else
+#define UNUSED_VAR
+#endif
+
+static char *file_prefix = NULL;
+
+
+struct event_store_list {
+       struct event_store *es;
+       struct event_store_list *next;
+};
+
+
+static struct event_store_list *store_list;
+static pthread_rwlock_t store_list_lock = PTHREAD_RWLOCK_INITIALIZER;
+
+
+/* ----------------
+ * helper functions
+ * ----------------
+ */
+static
+char *
+jobid2eventfile(const char *job_id_s)
+{
+  char *buffer;
+
+  if(job_id_s) {
+    asprintf(&buffer, "%s.%s", file_prefix, job_id_s);
+  } else 
+    asprintf(&buffer, "%s.default", file_prefix);
+    
+  return(buffer);
+}
+
+
+static
+char *
+jobid2controlfile(char *job_id_s)
+{
+  char *buffer;
+  char *hash;
+
+  if(job_id_s) {
+    asprintf(&buffer, "%s.%s.ctl", file_prefix, job_id_s);
+  } else 
+    asprintf(&buffer, "%s.default.ctl", file_prefix);
+    
+  return(buffer);
+}
+
+static
+int
+file_reader(void *user_data, char *buffer, const int len)
+{
+       size_t ret = 0;
+       
+       if(len > 0) {
+               ret = fread(buffer, 1, len, (FILE*)user_data);
+               if(ret == 0 && ferror((FILE*)user_data)) {
+                       return -1;
+               } 
+       }
+       return ret;
+}
+
+
+static
+int
+read_event_string(FILE *file, il_http_message_t *msg)
+{
+       int  len, ret;
+       int fd = fileno(file);
+       long start;
+
+       /* remember the start position */
+       start = ftell(file);
+       ret = receive_http(file, file_reader, msg);
+       if(ret < 0) return ret;
+       /* seek at the end of message in case the reader read ahead */
+       len = fseek(file, start + msg->len, SEEK_SET);
+       len = fgetc(file);
+       if(len != '\n') {
+               il_log(LOG_ERR, "error reading event from file, missing terminator character at %d, found %c(%d))\n", 
+                      start+msg->len, len, len);
+               if(msg->data) { free(msg->data); msg->data = NULL; }
+               if(msg->host) { free(msg->host); msg->host = NULL; }
+               return EINVAL;
+       }
+       return ret;
+}
+
+
+
+/* ------------------------------
+ * event_store 'member' functions
+ * ------------------------------
+ */
+static
+int
+event_store_free(struct event_store *es)
+{
+  assert(es != NULL);
+
+  if(es->job_id_s) free(es->job_id_s);
+  if(es->event_file_name) free(es->event_file_name);
+  if(es->control_file_name) free(es->control_file_name);
+  pthread_rwlock_destroy(&es->use_lock);
+  pthread_rwlock_destroy(&es->commit_lock);
+  free(es);
+
+  return(0);
+}
+
+
+static
+struct event_store *
+event_store_create(char *job_id_s)
+{
+  struct event_store *es;
+
+  es = malloc(sizeof(*es));
+  if(es == NULL) {
+    set_error(IL_NOMEM, ENOMEM, "event_store_create: error allocating room for structure");
+    return(NULL);
+  }
+
+  memset(es, 0, sizeof(*es));
+
+  il_log(LOG_DEBUG, "  creating event store for id %s\n", job_id_s);
+
+  es->job_id_s = strdup(job_id_s);
+  es->event_file_name = jobid2eventfile(job_id_s);
+  es->control_file_name = jobid2controlfile(job_id_s);
+
+  if(pthread_rwlock_init(&es->commit_lock, NULL)) 
+          abort();
+  if(pthread_rwlock_init(&es->use_lock, NULL)) 
+         abort();
+
+  return(es);
+}
+
+
+static
+int
+event_store_lock_ro(struct event_store *es)
+{
+  assert(es != NULL);
+
+  if(pthread_rwlock_rdlock(&es->commit_lock)) 
+    abort();
+
+  return(0);
+}
+
+
+static
+int
+event_store_lock(struct event_store *es)
+{
+  assert(es != NULL);
+
+  if(pthread_rwlock_wrlock(&es->commit_lock)) 
+    abort();
+
+  return(0);
+}
+
+
+static
+int
+event_store_unlock(struct event_store *es)
+{
+  assert(es != NULL);
+
+  if(pthread_rwlock_unlock(&es->commit_lock)) 
+    abort();
+  return(0);
+}
+
+
+static
+int
+event_store_read_ctl(struct event_store *es)
+{
+  FILE *ctl_file;
+
+  assert(es != NULL);
+
+  event_store_lock(es);
+  if((ctl_file = fopen(es->control_file_name, "r")) == NULL) {
+    /* no control file, new event file */
+    es->last_committed_ls = 0;
+    es->last_committed_bs = 0;
+  } else {
+    /* read last seen and last committed counts */
+    fscanf(ctl_file, "%*s\n%ld\n%ld\n",
+          &es->last_committed_ls,
+          &es->last_committed_bs);
+    fclose(ctl_file);
+  }
+  event_store_unlock(es);
+
+  return(0);
+}
+
+
+static
+int
+event_store_write_ctl(struct event_store *es)
+{
+  FILE   *ctl;
+
+  assert(es != NULL);
+
+  ctl = fopen(es->control_file_name, "w");
+  if(ctl == NULL) {
+    set_error(IL_SYS, errno, "event_store_write_ctl: error opening control file");
+    return(-1);
+  }
+
+  if(fprintf(ctl, "%s\n%ld\n%ld\n", 
+            es->job_id_s, 
+            es->last_committed_ls,
+            es->last_committed_bs) < 0) {
+    set_error(IL_SYS, errno, "event_store_write_ctl: error writing control record");
+    return(-1);
+  }
+
+  if(fclose(ctl) < 0) {
+    set_error(IL_SYS, errno, "event_store_write_ctl: error closing control file");
+    return(-1);
+  }
+
+  return(0);
+}
+
+
+/*
+ * event_store_qurantine() 
+ *   - rename damaged event store file 
+ *   - essentially does the same actions as cleanup, but the event store 
+ *     does not have to be empty
+ * returns 0 on success, -1 on error
+ */
+static
+int
+event_store_quarantine(struct event_store *es) 
+{
+       int num;
+       char newname[MAXPATHLEN+1];
+
+       /* find available qurantine name */
+       /* we give it at most 1024 tries */
+       for(num = 0; num < 1024; num++) {
+               struct stat st;
+
+               snprintf(newname, MAXPATHLEN, "%s.quarantine.%d", es->event_file_name, num);
+               newname[MAXPATHLEN] = 0;
+               if(stat(newname, &st) < 0) {
+                       if(errno == ENOENT) {
+                               /* file not found */
+                               break;
+                       } else {
+                               /* some other error with name, probably permanent */
+                               set_error(IL_SYS, errno, "event_store_qurantine: error looking for qurantine filename");
+                               return(-1);
+                               
+                       }
+               } else {
+                       /* the filename is used already */
+               }
+       }
+       if(num >= 1024) {
+               /* new name not found */
+               /* XXX - is there more suitable error? */
+               set_error(IL_SYS, ENOSPC, "event_store_quarantine: exhausted number of retries looking for quarantine filename");
+               return(-1);
+       }
+
+       /* actually rename the file */
+       il_log(LOG_DEBUG, "    renaming damaged event file from %s to %s\n",
+              es->event_file_name, newname);
+       if(rename(es->event_file_name, newname) < 0) {
+               set_error(IL_SYS, errno, "event_store_quarantine: error renaming event file");
+               return(-1);
+       }
+
+       /* clear the counters */
+       es->last_committed_ls = 0;
+       es->last_committed_bs = 0;
+       es->offset = 0;
+
+       return(0);
+}
+
+
+/*
+ * event_store_recover()
+ *   - recover after restart or catch up when events missing in IPC
+ *   - if offset > 0, read everything behind it
+ *   - if offset == 0, read everything behind min(last_committed_bs, last_committed_es)
+ */
+int
+event_store_recover(struct event_store *es)
+{
+  struct event_queue *eq_l = NULL, *eq_b = NULL;
+  struct server_msg *msg;
+  il_http_message_t hmsg;
+  char *event_s;
+  int fd, ret;
+  long last;
+  FILE *ef;
+  struct flock efl;
+  char err_msg[128];
+  struct stat stbuf;
+
+  assert(es != NULL);
+  
+#if defined(IL_NOTIFICATIONS)
+  /* destination queue has to be found for each message separately */
+#else
+  /* find bookkepping server queue */
+  eq_b = queue_list_get(es->job_id_s);
+  if(eq_b == NULL) 
+    return(-1);
+#endif
+
+#if !defined(IL_NOTIFICATIONS)
+  /* get log server queue */
+  eq_l = queue_list_get(NULL);
+#endif
+
+  event_store_lock(es);
+
+  il_log(LOG_DEBUG, "  reading events from %s\n", es->event_file_name);
+
+  /* open event file */
+  ef = fopen(es->event_file_name, "r");
+  if(ef == NULL) {
+         snprintf(err_msg, sizeof(err_msg), 
+                  "event_store_recover: error opening event file %s",
+                  es->event_file_name);
+         set_error(IL_SYS, errno, err_msg);
+         event_store_unlock(es);
+         return(-1);
+  }
+
+  /* lock the file for reading (we should not read while dglogd is writing) */
+  fd = fileno(ef);
+  efl.l_type = F_RDLCK;
+  efl.l_whence = SEEK_SET;
+  efl.l_start = 0;
+  efl.l_len = 0;
+  if(fcntl(fd, F_SETLKW, &efl) < 0) {
+         snprintf(err_msg, sizeof(err_msg), 
+                  "event_store_recover: error locking event file %s",
+                  es->event_file_name);
+         set_error(IL_SYS, errno, err_msg);
+         event_store_unlock(es);
+         fclose(ef);
+         return(-1);
+  }
+
+  /* check the file modification time and size to avoid unnecessary operations */
+  memset(&stbuf, 0, sizeof(stbuf));
+  if(fstat(fd, &stbuf) < 0) {
+         il_log(LOG_ERR, "    could not stat event file %s: %s\n", es->event_file_name, strerror(errno));
+         fclose(ef);
+         event_store_unlock(es);
+         return -1;
+  } else {
+         if((es->offset == stbuf.st_size) && (es->last_modified == stbuf.st_mtime)) {
+                 il_log(LOG_DEBUG, "  event file not modified since last visit, skipping\n");
+                 fclose(ef);
+                 event_store_unlock(es);
+                 return(0);
+         }
+  }
+
+  while(1) { /* try, try, try */
+
+         /* get the position in file to be sought */
+         if(es->offset)
+                 last = es->offset;
+         else {
+                 last = es->last_committed_bs;
+         }
+
+         il_log(LOG_DEBUG, "    setting starting file position to  %ld\n", last);
+         il_log(LOG_DEBUG, "    bytes sent to destination: %d\n", es->last_committed_bs);
+
+         if(last > 0) {
+                 int c;
+
+                 /* skip all committed or already enqueued events */
+                 /* be careful - check, if the offset really points to the
+                    beginning of event string */
+                 if(fseek(ef, last - 1, SEEK_SET) < 0) {
+                         set_error(IL_SYS, errno, "event_store_recover: error setting position for read");
+                         event_store_unlock(es);
+                         fclose(ef);
+                         return(-1);
+                 }
+                 /* the last enqueued event MUST end with \n */
+                 if((c=fgetc(ef)) != '\n') {
+                         /* Houston, we have got a problem */
+                         il_log(LOG_WARNING, 
+                                "    file position %ld does not point at the beginning of event string, backing off!\n",
+                                last);
+                         /* now, where were we? */
+                         if(es->offset) {
+                                 /* next try will be with
+                                    last_commited_bs */
+                                 es->offset = 0;
+                         } else {
+                                 /* this is really weird... back off completely */
+                                 es->last_committed_ls = es->last_committed_bs = 0;
+                         }
+                 } else {
+                         /* OK, break out of the loop */
+                         break;
+                 }
+         } else {
+                 /* this breaks out of the loop, we are starting at
+                  * the beginning of file
+                  */
+                 if(fseek(ef, 0, SEEK_SET) < 0) {
+                         set_error(IL_SYS, errno, "event_store_recover: error setting position for read");
+                         event_store_unlock(es);
+                         fclose(ef);
+                         return(-1);
+                 }
+                 break;
+         }
+  }
+
+  /* enqueue all remaining events */
+  ret = 1;
+  msg = NULL;
+  while(read_event_string(ef, &hmsg) >= 0) {
+       
+    /* last holds the starting position of event_s in file */
+    il_log(LOG_DEBUG, "    reading event at %ld\n", last);
+
+    /* break from now on means there was some error */
+    ret = -1;
+
+    /* create message for server */
+    msg = server_msg_create((il_octet_string_t*)&hmsg, last);
+    if(msg == NULL) {
+           il_log(LOG_ALERT, "    event file corrupted! I will try to move it to quarantine (ie. rename it).\n");
+           /* actually do not bother if quarantine succeeded or not - we could not do more */
+           event_store_quarantine(es);
+           fclose(ef);
+           event_store_unlock(es);
+           return(-1);
+    }
+    msg->es = es;
+
+    /* first enqueue to the LS */
+    if(!bs_only && (last >= es->last_committed_ls)) {
+      
+           il_log(LOG_DEBUG, "      queueing event at %ld to server %s\n", last, eq_l->dest_name);
+
+#if !defined(IL_NOTIFICATIONS)
+      if(enqueue_msg(eq_l, msg) < 0)
+       break;
+#endif
+      }
+
+#ifdef IL_NOTIFICATIONS
+    eq_b = queue_list_get(msg->dest);
+#endif
+
+    /* now enqueue to the BS, if neccessary */
+    if((eq_b != eq_l) && 
+       (last >= es->last_committed_bs)) {
+      
+           il_log(LOG_DEBUG, "      queueing event at %ld to server %s\n", last, eq_b->dest_name);
+      
+      if(enqueue_msg(eq_b, msg) < 0)
+       break;
+    }
+    server_msg_free(msg);
+    msg = NULL;
+
+    /* now last is also the offset behind the last successfully queued event */
+    last = ftell(ef);
+
+    /* ret == 0 means EOF or incomplete event found */
+    ret = 0;
+
+  } /* while */
+
+  /* due to this little assignment we had to lock the event_store for writing */
+  es->offset = last;
+  es->last_modified = stbuf.st_mtime;
+  il_log(LOG_DEBUG, "  event store offset set to %ld\n", last);
+
+  if(msg) 
+    server_msg_free(msg);
+
+  fclose(ef);
+  il_log(LOG_DEBUG, "  finished reading events with %d\n", ret);
+
+  event_store_unlock(es);
+  return(ret);
+}
+
+
+/*
+ * event_store_sync()
+ *   - check the position of event and fill holes from file
+ *   - return 1 if the event is new,
+ *            0 if it was seen before,
+ *           -1 if there was an error
+ */
+int
+event_store_sync(struct event_store *es, long offset)
+{
+       int ret;
+
+       assert(es != NULL);
+
+       /* all events are actually read from file, the event on socket
+        * is ignored and serves just to notify us about file change
+        */
+       ret = event_store_recover(es);
+       ret = (ret < 0) ? ret : 0;
+       return(ret);
+}
+
+
+int
+event_store_next(struct event_store *es, long offset, int len)
+{
+       assert(es != NULL);
+  
+       /* offsets are good only to detect losses (differences between socket and file),
+          which is not possible now */
+       return 0;
+}
+
+
+/* 
+ * event_store_commit()
+ *
+ */
+int
+event_store_commit(struct event_store *es, int len, int ls)
+{
+  assert(es != NULL);
+
+  event_store_lock(es);
+
+  if(ls)
+    es->last_committed_ls += len;
+  else {
+    es->last_committed_bs += len;
+    if (bs_only) es->last_committed_ls += len;
+  }
+
+  if(event_store_write_ctl(es) < 0) {
+    event_store_unlock(es);
+    return(-1);
+  }
+
+  event_store_unlock(es);
+
+
+  return(0);
+}
+
+
+/*
+ * event_store_clean()
+ *  - remove the event files (event and ctl), if they are not needed anymore
+ *  - returns 0 if event_store is in use, 1 if it was removed and -1 on error
+ *
+ * Q: How do we know that we can safely remove the files?
+ * A: When all events from file have been committed both by LS and BS.
+ */
+static 
+int
+event_store_clean(struct event_store *es)
+{
+  long last;
+  int fd;
+  FILE *ef;
+  struct flock efl;
+
+  assert(es != NULL);
+
+  /* prevent sender threads from updating */
+  event_store_lock(es);
+  
+  il_log(LOG_DEBUG, "  trying to cleanup event store %s\n", es->job_id_s);
+  il_log(LOG_DEBUG, "    bytes sent to logging server: %d\n", es->last_committed_ls);
+  il_log(LOG_DEBUG, "    bytes sent to bookkeeping server: %d\n", es->last_committed_bs);
+
+  /* preliminary check to avoid opening event file */
+  /* if the positions differ, some events still have to be sent */
+  if(es->last_committed_ls != es->last_committed_bs) {
+    event_store_unlock(es);
+    il_log(LOG_DEBUG, "  not all events sent, cleanup aborted\n");
+    return(0);
+  }
+
+  /* the file can only be removed when all the events were succesfully sent 
+     (ie. committed both by LS and BS */
+  /* That also implies that the event queues are 'empty' at the moment. */
+  ef = fopen(es->event_file_name, "r+");
+  if(ef == NULL) {
+    /* if we can not open the event store, it is an error and the struct should be removed */
+    /* XXX - is it true? */
+    event_store_unlock(es);
+    il_log(LOG_ERR,  "  event_store_clean: error opening event file: %s\n", strerror(errno));
+    return(1);
+  }
+  
+  fd = fileno(ef);
+  
+  /* prevent local-logger from writing into event file */
+  efl.l_type = F_WRLCK;
+  efl.l_whence = SEEK_SET;
+  efl.l_start = 0;
+  efl.l_len = 0;
+  if(fcntl(fd, F_SETLK, &efl) < 0) {
+    il_log(LOG_DEBUG, "    could not lock event file, cleanup aborted\n");
+    fclose(ef);
+    event_store_unlock(es);
+    if(errno != EACCES &&
+       errno != EAGAIN) {
+      set_error(IL_SYS, errno, "event_store_clean: error locking event file");
+      return(-1);
+    }
+    return(0);
+  }
+  
+  /* now the file should not contain partially written event, so it is safe
+     to get offset behind last event by seeking the end of file */
+  if(fseek(ef, 0, SEEK_END) < 0) {
+    set_error(IL_SYS, errno, "event_store_clean: error seeking the end of file");
+    event_store_unlock(es);
+    fclose(ef);
+    return(-1);
+  }
+  
+  last = ftell(ef);
+  il_log(LOG_DEBUG, "    total bytes in file: %d\n", last);
+
+  if(es->last_committed_ls < last) {
+    fclose(ef);
+    event_store_unlock(es);
+    il_log(LOG_DEBUG, "    events still waiting in queue, cleanup aborted\n");
+    return(0);
+  } else if( es->last_committed_ls > last) {
+         il_log(LOG_WARNING, "  warning: event file seems to shrink!\n");
+         /* XXX - in that case we can not continue because there may be
+            some undelivered events referring to that event store */
+         fclose(ef);
+         event_store_unlock(es);
+         return(0);
+  }
+  
+  /* now we are sure that all events were sent and the event queues are empty */
+  il_log(LOG_INFO, "    removing event file %s\n", es->event_file_name);
+  
+  /* remove the event file */
+  unlink(es->event_file_name);
+  unlink(es->control_file_name);
+  
+  /* clear the counters */
+  es->last_committed_ls = 0;
+  es->last_committed_bs = 0;
+  es->offset = 0;
+
+  /* unlock the event_store even if it is going to be removed */
+  event_store_unlock(es);
+
+  /* close the event file (that unlocks it as well) */
+  fclose(ef);
+
+  /* indicate that it is safe to remove this event_store */
+  return(1);
+}
+
+
+/* --------------------------------
+ * event store management functions
+ * --------------------------------
+ */
+struct event_store *
+event_store_find(char *job_id_s)
+{
+  struct event_store_list *q, *p;
+  struct event_store *es;
+
+  if(pthread_rwlock_wrlock(&store_list_lock)) {
+         abort();
+  }
+
+  es = NULL;
+  
+  q = NULL;
+  p = store_list;
+  
+  while(p) {
+    if(strcmp(p->es->job_id_s, job_id_s) == 0) {
+      es = p->es;
+      if(pthread_rwlock_rdlock(&es->use_lock))
+             abort();
+      if(pthread_rwlock_unlock(&store_list_lock)) 
+             abort();
+      return(es);
+    }
+
+    q = p;
+    p = p->next;
+  }
+
+  es = event_store_create(job_id_s);
+  if(es == NULL) {
+         if(pthread_rwlock_unlock(&store_list_lock)) 
+                 abort();
+         return(NULL);
+  }
+
+  p = malloc(sizeof(*p));
+  if(p == NULL) {
+    set_error(IL_NOMEM, ENOMEM, "event_store_find: no room for new event store");
+      if(pthread_rwlock_unlock(&store_list_lock)) 
+             abort();
+    return(NULL);
+  }
+  
+  p->next = store_list;
+  store_list = p;
+    
+  p->es = es;
+
+  if(pthread_rwlock_rdlock(&es->use_lock))
+         abort();
+
+  if(pthread_rwlock_unlock(&store_list_lock)) 
+         abort();
+
+  return(es);
+}
+
+
+int
+event_store_release(struct event_store *es)
+{
+       assert(es != NULL);
+
+       if(pthread_rwlock_unlock(&es->use_lock))
+               abort();
+       il_log(LOG_DEBUG, "  released lock on %s\n", es->job_id_s);
+       return(0);
+}
+
+
+event_store_from_file(char *filename)
+{
+       struct event_store *es;
+       FILE *event_file;
+       char *job_id_s = NULL, *p;
+       il_http_message_t hmsg;
+       int ret;
+       
+       il_log(LOG_INFO, "  attaching to event file: %s\n", filename);
+       
+       if(strstr(filename, "quarantine") != NULL) {
+               il_log(LOG_INFO, "  file name belongs to quarantine, not touching that.\n");
+               return(0);
+       }
+
+       event_file = fopen(filename, "r");
+       if(event_file == NULL) {
+               set_error(IL_SYS, errno, "event_store_from_file: error opening event file");
+               return(-1);
+       }
+       ret = read_event_string(event_file, &hmsg);
+       fclose(event_file);
+       if(ret < 0) 
+               return(0);
+       
+       /* get id aka dest */
+       job_id_s = hmsg.host;
+
+       il_log(LOG_DEBUG, "  message dest: '%s'\n", job_id_s);
+       if(job_id_s == NULL) {
+               il_log(LOG_NOTICE, "  skipping file, could not parse event\n");
+               ret = 0;
+               goto out;
+       }
+       
+       es=event_store_find(job_id_s);
+       
+       if(es == NULL) {
+               ret = -1;
+               goto out;
+       }
+
+       if((es->last_committed_ls == 0) &&
+          (es->last_committed_bs == 0) &&
+          (es->offset == 0)) {
+               ret = event_store_read_ctl(es);
+       } else 
+               ret = 0;
+       
+       event_store_release(es);
+
+out:
+       if(hmsg.data) free(hmsg.data);
+       if(job_id_s) free(job_id_s);
+       return(ret);
+}
+
+
+int
+event_store_init(char *prefix)
+{
+  if(file_prefix == NULL) {
+    file_prefix = strdup(prefix);
+    store_list = NULL;
+  }
+
+  /* read directory and get a list of event files */
+  {
+    int len;
+
+    char *p, *dir;
+    DIR *event_dir;
+    struct dirent *entry;
+
+
+    /* get directory name */
+    p = strrchr(file_prefix, '/');
+    if(p == NULL) {
+      dir = strdup(".");
+      p = "";
+      len = 0;
+    } else {
+      *p = '\0';
+      dir = strdup(file_prefix);
+      *p++ = '/';
+      len = strlen(p);
+    }
+
+    event_dir = opendir(dir);
+    if(event_dir == NULL) {
+      free(dir);
+      set_error(IL_SYS, errno, "event_store_init: error opening event directory");
+      return(-1);
+    }
+    
+    while((entry=readdir(event_dir))) {
+      char *s;
+
+      /* skip all files that do not match prefix */
+      if(strncmp(entry->d_name, p, len) != 0) 
+       continue;
+
+      /* skip all control files */
+      if((s=strstr(entry->d_name, ".ctl")) != NULL &&
+        s[4] == '\0')
+       continue;
+
+      s = malloc(strlen(dir) + strlen(entry->d_name) + 2);
+      if(s == NULL) {
+       free(dir);
+       set_error(IL_NOMEM, ENOMEM, "event_store_init: no room for file name");
+       return(-1);
+      }
+
+      *s = '\0';
+      strcat(s, dir);
+      strcat(s, "/");
+      strcat(s, entry->d_name);
+
+      if(event_store_from_file(s) < 0) {
+       free(dir);
+       free(s);
+       closedir(event_dir);
+       return(-1);
+      }
+
+      free(s);
+    }
+    closedir(event_dir);
+
+    /* one more pass - this time remove stale .ctl files */
+    event_dir = opendir(dir);
+    if(event_dir == NULL) {
+      free(dir);
+      set_error(IL_SYS, errno, "event_store_init: error opening event directory");
+      return(-1);
+    }
+    
+    while((entry=readdir(event_dir))) {
+      char *s;
+
+      /* skip all files that do not match prefix */
+      if(strncmp(entry->d_name, p, len) != 0) 
+       continue;
+
+      /* find all control files */
+      if((s=strstr(entry->d_name, ".ctl")) != NULL &&
+        s[4] == '\0') {
+             char *ef;
+             struct stat st;
+
+             /* is there corresponding event file? */
+             ef = malloc(strlen(dir) + strlen(entry->d_name) + 2);
+             if(ef == NULL) {
+                     free(dir);
+                     set_error(IL_NOMEM, ENOMEM, "event_store_init: no room for event file name");
+                     return(-1);
+             }
+
+             s[0] = 0;
+             *ef = '\0';
+             strcat(ef, dir);
+             strcat(ef, "/");
+             strcat(ef, entry->d_name);
+             s[0] = '.';
+
+             if(stat(ef, &st) == 0) {
+                     /* something is there */
+                     /* XXX - it could be something else than event file, but do not bother now */
+             } else {
+                     /* could not stat file, remove ctl */
+                     strcat(ef, s);
+                     il_log(LOG_DEBUG, "  removing stale file %s\n", ef);
+                     if(unlink(ef)) 
+                             il_log(LOG_ERR, "  could not remove file %s: %s\n", ef, strerror(errno));
+                     
+             }
+             free(ef);
+
+      }
+    }
+    closedir(event_dir);
+    free(dir);
+  }
+
+  return(0);
+}
+
+
+int
+event_store_recover_all()
+{
+  struct event_store_list *sl;
+
+
+  if(pthread_rwlock_rdlock(&store_list_lock)) 
+         abort();
+
+  /* recover all event stores */
+  sl = store_list;
+  while(sl != NULL) {
+
+         /* recover this event store */
+         /* no need to lock use_lock in event_store, the store_list_lock is in place */
+         if(event_store_recover(sl->es) < 0) {
+                 il_log(LOG_ERR, "  error recovering event store %s:\n    %s\n", sl->es->event_file_name, error_get_msg());
+                 clear_error();
+         }
+         sl = sl->next;
+  }
+  
+  if(pthread_rwlock_unlock(&store_list_lock)) 
+         abort();
+
+  return(0);
+}
+
+
+#if 0 
+int
+event_store_remove(struct event_store *es)
+{
+  struct event_store_list *p, **q;
+
+  assert(es != NULL);
+
+  switch(event_store_clean(es)) {
+  case 0:
+    il_log(LOG_DEBUG, "  event store not removed, still used\n");
+    return(0);
+    
+  case 1:
+    if(pthread_rwlock_wrlock(&store_list_lock) < 0) {
+      set_error(IL_SYS, errno, "  event_store_remove: error locking event store list");
+      return(-1);
+    }
+
+    p = store_list;
+    q = &store_list;
+
+    while(p) {
+      if(p->es == es) {
+       (*q) = p->next;
+       event_store_free(es);
+       free(p);
+       break;
+      }
+      q = &(p->next);
+      p = p->next;
+    }
+
+    if(pthread_rwlock_unlock(&store_list_lock) < 0) {
+      set_error(IL_SYS, errno, "  event_store_remove: error unlocking event store list");
+      return(-1);
+    }
+    return(1);
+
+  default:
+    return(-1);
+  }
+  /* not reached */
+  return(0);
+}
+#endif
+
+int
+event_store_cleanup()
+{
+  struct event_store_list *sl;
+  struct event_store_list *slnext;
+  struct event_store_list **prev;
+
+  /* try to remove event files */
+
+  if(pthread_rwlock_wrlock(&store_list_lock)) 
+         abort();
+
+  sl = store_list;
+  prev = &store_list;
+
+  while(sl != NULL) {
+         int ret;
+
+         slnext = sl->next;
+         
+         /* one event store at time */
+         ret = pthread_rwlock_trywrlock(&sl->es->use_lock);
+         if(ret == EBUSY) {
+                 il_log(LOG_DEBUG, "  event_store %s is in use by another thread\n", 
+                        sl->es->job_id_s);
+                 sl = slnext;
+                 continue;
+         } else if (ret < 0)
+           abort();
+
+         switch(event_store_clean(sl->es)) {
+                 
+         case 1:
+                 /* remove this event store */
+                 (*prev) = slnext;
+                 event_store_free(sl->es);
+                 free(sl);
+                 break;
+                 
+         case -1:
+                 il_log(LOG_ERR, "  error removing event store %s (file %s):\n    %s\n", 
+                        sl->es->job_id_s, sl->es->event_file_name, error_get_msg());
+                 /* event_store_release(sl->es); */
+                 clear_error();
+                 /* go on to the next */
+                 
+         default:
+                 event_store_release(sl->es);
+                 prev = &(sl->next);
+                 break;
+         }
+         
+         sl = slnext;
+  }
+  
+  if(pthread_rwlock_unlock(&store_list_lock)) 
+         abort();
+  
+  return(0);
+}
+
diff --git a/org.glite.lb.logger/src/http.c b/org.glite.lb.logger/src/http.c
new file mode 100644 (file)
index 0000000..2480872
--- /dev/null
@@ -0,0 +1,214 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <string.h>
+#include <errno.h>
+
+#include "interlogd.h"
+
+
+int 
+parse_request(const char *s, il_http_message_t *msg)
+{
+       if(!strncasecmp(s, "HTTP", 4)) {
+               msg->msg_type = IL_HTTP_REPLY;
+       } else if(!strncasecmp(s, "POST", 4)) {
+               msg->msg_type = IL_HTTP_POST;
+       } else if(!strncasecmp(s, "GET", 3)) {
+               msg->msg_type = IL_HTTP_GET;
+       } else {
+               msg->msg_type = IL_HTTP_OTHER;
+       }
+       if(msg->msg_type == IL_HTTP_REPLY) {
+               char *p = strchr(s, ' ');
+
+               if(!p) goto parse_end;
+               p++;
+               msg->reply_code=atoi(p);
+               p = strchr(p, ' ');
+               if(!p) goto parse_end;
+               p++;
+               msg->reply_string = strdup(p);
+
+       parse_end:
+               ;
+       }
+}
+
+
+int
+parse_header(const char *s, il_http_message_t *msg)
+{
+       if(!strncasecmp(s, "Content-Length:", 15)) {
+               msg->content_length = atoi(s + 15);
+       } else if(!strncasecmp(s, "Host:", 5)) {
+               const char *p = s + 4;
+               while(*++p == ' '); /* skip spaces */
+               msg->host = strdup(p);
+       }
+       return(0);
+}
+
+
+#define DEFAULT_CHUNK_SIZE 1024
+
+// read what is available and parse what can be parsed
+// returns the result of read operation of the underlying connection,
+// ie. the number of bytes read or error code
+int
+receive_http(void *user_data, int (*reader)(void *, char *, const int), il_http_message_t *msg)
+{
+       static enum { NONE, IN_REQUEST, IN_HEADERS, IN_BODY } state = NONE;
+       int  len, alen, clen, i, buffer_free, min_buffer_free = DEFAULT_CHUNK_SIZE;
+       char *buffer, *p, *s, *cr;
+       
+       memset(msg, 0, sizeof(*msg));
+       // msg->data = NULL;
+       // msg->len = 0;
+       state = IN_REQUEST;
+       alen = 0;
+       buffer = NULL;
+       buffer_free = 0;
+       p = NULL;
+       s = NULL;
+
+       do {
+               /* p - first empty position in buffer
+                  alen - size of allocated buffer
+                  len - number of bytes received in last read
+                  s - points behind last scanned CRLF or at buffer start 
+                  buffer_free = alen - (p - buffer) 
+               */
+
+               /* prepare at least chunk_size bytes for next data */
+               if(buffer_free < min_buffer_free) {
+                       char *n;
+                       
+                       alen += min_buffer_free;
+                       n = realloc(buffer, alen);
+                       if(n == NULL) {
+                               free(buffer);
+                               set_error(IL_NOMEM, ENOMEM, "read_event: no room for event");
+                               return(-1);
+                       }
+                       buffer_free += min_buffer_free;
+                       p = n + (p - buffer);
+                       s = n + (s - buffer);
+                       buffer = n;
+               }
+
+               if(buffer_free > 0) {
+                       len = (*reader)(user_data, p, buffer_free); 
+                       if(len < 0) {
+                               // error
+                               free(buffer);
+                               // set_error(IL_SYS, errno, "receive_http: error reading data");
+                               return -1;
+                       } else if(len == 0) {
+                               // EOF
+                               free(buffer);
+                               set_error(IL_PROTO, errno, "receive_http: error reading data - premature EOF");
+                               return -1;
+                       }
+                       buffer_free -= len;
+                       p+= len;
+               }
+
+
+               switch(state) {
+
+                       // parse buffer, look for CRLFs
+                       //   s - start scan position
+                       //   p - start of current token
+                       //   cr - current CRLF position
+
+               case IN_REQUEST:
+                       if((s < p - 1) &&
+                          (cr = (char*)memchr(s, '\r', p - s - 1)) &&
+                          (cr[1] == '\n')) {
+                               *cr = 0;
+                               parse_request(s, msg);
+                               *cr = '\r';
+                               // change state
+                               state = IN_HEADERS;
+                               // start new tokens (cr < p - 1 -> s < p + 1 <-> s <= p)
+                               s = cr + 2;
+                       } else {
+                         break;
+                       }
+
+               case IN_HEADERS:  
+                       while((state != IN_BODY) &&
+                             (s < p - 1) && 
+                             (cr = (char*)memchr(s, '\r', p - s - 1)) &&
+                             (cr[1] == '\n')) {
+                               if(s == cr) { /* do not consider request starting with CRLF */
+                                       // found CRLFCRLF
+                                       state = IN_BODY;
+                               } else {
+                                       *cr = 0;
+                                       parse_header(s, msg);
+                                       *cr = '\r';
+                               }
+                               // next scan starts after CRLF
+                               s = cr + 2; 
+                       }
+                       if(state == IN_BODY) {
+                               // we found body
+                               // content-length should be set at the moment
+                               if(msg->content_length > 0) {
+                                       int need_free = msg->content_length - (p - s);
+                                       char *n;
+                       
+                                       alen += need_free - buffer_free + 1;
+                                       n = realloc(buffer, alen);
+                                       if(n == NULL) {
+                                               free(buffer);
+                                               set_error(IL_NOMEM, ENOMEM, "read_event: no room for event");
+                                               return(-1);
+                                       }
+                                       buffer_free = need_free;
+                                       min_buffer_free = 0;
+                                       p = n + (p - buffer);
+                                       s = n + (s - buffer);
+                                       buffer = n;
+                               } else {
+                                       // report error
+                                       free(buffer);
+                                       set_error(IL_PROTO, EINVAL, "receive_http: error reading data - no content length specified\n");
+                                       return -1;
+                               }
+                       }
+                       break;
+                       
+               case IN_BODY:
+                       if(buffer_free == 0) {
+                               // finished reading
+                               *p = 0;
+                               state = NONE;
+                       }
+                       break;
+               }
+       } while(state != NONE);
+       
+       msg->data = buffer;
+       msg->len = p - buffer;
+
+       return 0;
+}
index 1fe9bb9..b9a5647 100644 (file)
@@ -1,4 +1,21 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <errno.h>
 #include <stdio.h>
 extern void _start (void), etext (void);
 #endif
 
-/* XXX DK: */
-#include <err.h> // SSL header file 
-
 #include "glite/security/glite_gss.h"
-
 #include "il_error.h"
 
 
-extern int log_level;
-
 static pthread_key_t err_key;
 
 static int IL_ERR_MSG_LEN = 1024;
@@ -31,6 +42,8 @@ static
 void
 error_key_delete(void *err)
 {
+  if(((struct error_inf*)err)->msg)
+    free(((struct error_inf*)err)->msg);
   free(err);
 }
 
@@ -56,7 +69,7 @@ error_get_err ()
 
 
 int
-init_errors(int level)
+init_errors()
 {
   static pthread_once_t error_once = PTHREAD_ONCE_INIT;
   struct error_inf *err;
@@ -80,9 +93,6 @@ init_errors(int level)
   if(err->msg == NULL) 
          return(-1);
 
-  if(level)
-    log_level = level;
-
 #ifdef LB_PROF
   monstartup((u_long)&_start, (u_long)&etext);
 #endif
@@ -110,11 +120,6 @@ set_error(int code, long minor, char *msg)
     snprintf(err->msg, IL_ERR_MSG_LEN, "%s: %s", msg, hstrerror(err->code_min));
     break;
 
-  /* XXX DK: je tahle hodnota k necemu potreba? */
-  case IL_AUTH:
-    snprintf(err->msg, IL_ERR_MSG_LEN, "%s: %s", msg, ERR_error_string(err->code_min, NULL));
-    break;
-
   case IL_DGGSS:
     switch(err->code_min) {
 
diff --git a/org.glite.lb.logger/src/il_error.h b/org.glite.lb.logger/src/il_error.h
deleted file mode 100644 (file)
index 120e7ed..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-#ifndef IL_ERROR_H
-#define IL_ERROR_H
-
-#ident "$Header$"
-
-#include <syslog.h>
-
-enum err_code_maj { /* minor =                   */
-  IL_OK,            /*     0                     */
-  IL_SYS,           /*     errno                 */
-  IL_NOMEM,         /*     ENOMEM                */
-  IL_AUTH,          /*     0 (SSL error)         */
-  IL_PROTO,         /*     LB_*                  */
-  IL_LBAPI,         /*     dgLBErrCode           */
-  IL_DGGSS,         /*     EDG_WLL_GSS_*         */
-  IL_HOST           /*     h_errno               */
-};
-
-struct error_inf {
-  int  code_maj;
-  long code_min;
-  char *msg;
-};
-
-int init_errors(int);
-int set_error(int, long, char *);
-int clear_error();
-int error_get_maj();
-long error_get_min();
-char *error_get_msg();
-
-int il_log(int, char *, ...);
-
-#endif
index e394e5a..2cc1018 100644 (file)
@@ -1,10 +1,28 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <errno.h>
 #include <string.h>
 #include <unistd.h>
+#include <stdio.h>
 
-#include "glite/wmsutils/jobid/cjobid.h"
+#include "glite/jobid/cjobid.h"
 #include "glite/lb/context.h"
 #include "glite/lb/events_parse.h"
 #include "glite/lb/il_string.h"
 #include "glite/lb/lb_perftest.h"
 #endif
 
-static
 int
-cmp_jobid(struct server_msg *msg, void *data) 
-{
-       char *job_id_s = (char*)data;
-       return strcmp(msg->job_id_s, job_id_s) == 0;
-}
-
-static
-int
-cmp_jobid_set_exp(struct server_msg *msg, void *data)
-{
-       struct server_msg *m = (struct server_msg *)data;
-
-       if(strcmp(msg->job_id_s, m->job_id_s) == 0) {
-               msg->expires = m->expires;
-       }
-       return 0;
-}
-
-
-int 
 enqueue_msg(struct event_queue *eq, struct server_msg *msg)
 {
-#if defined(IL_NOTIFICATIONS)
-       struct event_queue *eq_known;
-
-       /* now we have a new event with possibly changed destination,
-          so check for the already known destination and possibly move 
-          events from the original output queue to a new one */
-       eq_known = notifid_map_get_dest(msg->job_id_s);
-       if(eq != eq_known) {
-               /* client has changed delivery address for this notification */
-               if(notifid_map_set_dest(msg->job_id_s, eq) < 0) 
-                       return(-1);
-               /* move all events with this notif_id from eq_known to eq */
-               if(eq_known != NULL) {
-                       event_queue_move_events(eq_known, eq, cmp_jobid, msg->job_id_s);
-                       /* XXX - we should kill the old queue too */
-               }
-       }
-
-       /* if the expiration changed, set new one */
-       if(msg->expires != notifid_map_get_expiration(msg->job_id_s)) {
-               notifid_map_set_expiration(msg->job_id_s, msg->expires);
-               /* set expiration for all events with this notif id */
-               event_queue_move_events(eq, NULL, cmp_jobid_set_exp, msg);
-       }
-#endif
+       int ret;
 
        /* fire thread to take care of this queue */
-       if(event_queue_create_thread(eq) < 0) 
+       if(event_queue_create_thread(eq) < 0)
                return(-1);
-       
+
 #if defined(IL_NOTIFICATIONS)
-       /* if there are no data to send, do not send anything 
+       /* if there are no data to send, do not send anything
           (messsage was just to change the delivery address) */
        /* CORRECTION - let the message pass through the output queue
           to commit it properly and keep event_store in sync */
-       /* if(msg->len == 0) 
+       /* if(msg->len == 0)
                return(0);
        */
 #endif
@@ -82,18 +55,18 @@ enqueue_msg(struct event_queue *eq, struct server_msg *msg)
        event_queue_cond_lock(eq);
 
        /* insert new event */
-       if(event_queue_insert(eq, msg) < 0) {
+       if((ret = event_queue_insert(eq, msg)) < 0) {
                event_queue_cond_unlock(eq);
-               return(-1);
+               return ret;
        }
-      
+
        /* signal thread that we have a new message */
        event_queue_signal(eq);
 
        /* allow thread to continue */
        event_queue_cond_unlock(eq);
 
-       return(0);
+       return ret;
 }
 
 
@@ -103,7 +76,7 @@ pthread_cond_t flush_cond = PTHREAD_COND_INITIALIZER;
 #endif /* INTERLOGD_FLUSH */
 
 #ifdef INTERLOGD_HANDLE_CMD
-static 
+static
 int
 parse_cmd(char *event, char **job_id_s, long *receipt, int *timeout)
 {
@@ -125,10 +98,11 @@ parse_cmd(char *event, char **job_id_s, long *receipt, int *timeout)
                        continue;
                }
                if(strncmp(token, "DG.COMMAND", r - token) == 0) {
-#if defined(INTERLOGD_FLUSH)                   
+#if defined(INTERLOGD_FLUSH)
                        if(strcmp(++r, "\"flush\"")) {
 #endif
-                               il_log(LOG_WARNING, "  command %s not implemented\n", r);
+                               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, "command %s not implemented", 
+                                                r);
                                ret = -1;
                                continue;
 #if defined(INTERLOGD_FLUSH)
@@ -136,7 +110,7 @@ parse_cmd(char *event, char **job_id_s, long *receipt, int *timeout)
 #endif
                } else if(strncmp(token, "DG.JOBID", r - token) == 0) {
                        char  *p;
-      
+
                        r += 2; /* skip =" */
                        p = index(r, '"');
                        if(p == NULL) { ret = -1; continue; }
@@ -147,7 +121,7 @@ parse_cmd(char *event, char **job_id_s, long *receipt, int *timeout)
                } else if(strncmp(token, "DG.LLLID", r - token) == 0) {
                        sscanf(++r, "%ld", receipt);
                }
-    
+
        }
        return(0);
 }
@@ -159,8 +133,8 @@ parse_cmd(char *event, char **job_id_s, long *receipt, int *timeout)
  *  -1 - failure
  */
 
-static 
-int 
+static
+int
 handle_cmd(il_octet_string_t *event, long offset)
 {
        char *job_id_s;
@@ -172,15 +146,15 @@ handle_cmd(il_octet_string_t *event, long offset)
        struct timeval  tv;
 
        /* parse command */
-       if(parse_cmd(event->data, &job_id_s, &receipt, &timeout) < 0) 
+       if(parse_cmd(event->data, &job_id_s, &receipt, &timeout) < 0)
                return(0);
 
 #if defined(INTERLOGD_FLUSH)
-       il_log(LOG_DEBUG, "  received FLUSH command\n");
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, "received FLUSH command");
 
        /* catchup with all neccessary event files */
        if(job_id_s) {
-               struct event_store *es = event_store_find(job_id_s);
+               struct event_store *es = event_store_find(job_id_s, NULL);
 
                if(es == NULL) {
                        goto cmd_error;
@@ -190,15 +164,16 @@ handle_cmd(il_octet_string_t *event, long offset)
                   no need to lock the event_store at all */
                event_store_release(es);
                if(result < 0) {
-                       il_log(LOG_ERR, "  error trying to catch up with event file: %s\n",
-                              error_get_msg());
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                        "  error trying to catch up with event file: %s",
+                                        error_get_msg());
                        clear_error();
                }
-       } else 
+       } else
          /* this call does not fail :-) */
          event_store_recover_all();
 
-       il_log(LOG_DEBUG, "  alerting threads to report status\n");
+       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, "  alerting threads to report status");
 
        /* prevent threads from reporting too early */
        if(pthread_mutex_lock(&flush_lock) < 0) {
@@ -254,11 +229,13 @@ handle_cmd(il_octet_string_t *event, long offset)
        while(num_replies < num_threads) {
                int ret;
                if((ret=pthread_cond_timedwait(&flush_cond, &flush_lock, &endtime)) < 0) {
-                       il_log(LOG_ERR, "    error waiting for thread reply: %s\n", strerror(errno));
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                        "    error waiting for thread reply: %s", 
+                                        strerror(errno));
                        result = (ret == ETIMEDOUT) ? 0 : -1;
                        break;
                }
-               
+
                /* collect results from reporting threads */
                if(job_id_s) {
                        /* find appropriate queue */
@@ -269,7 +246,7 @@ handle_cmd(il_octet_string_t *event, long offset)
                                if(eq->flushing == 2) {
                                        eq->flushing = 0;
                                        num_replies++;
-                                       result = ((result == 1) || (eq->flush_result < 0))  ? 
+                                       result = ((result == 1) || (eq->flush_result < 0))  ?
                                                eq->flush_result : result;
                                }
                                event_queue_cond_unlock(eq);
@@ -282,8 +259,10 @@ handle_cmd(il_octet_string_t *event, long offset)
                                        if(eq->flushing == 2) {
                                                eq->flushing = 0;
                                                num_replies++;
-                                               il_log(LOG_DEBUG, "    thread reply: %d\n", eq->flush_result);
-                                               result = ((result == 1) || (eq->flush_result < 0))  ? 
+                                               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                                                "    thread reply: %d", 
+                                                                eq->flush_result);
+                                               result = ((result == 1) || (eq->flush_result < 0))  ?
                                                        eq->flush_result : result;
                                        }
                                        event_queue_cond_unlock(eq);
@@ -297,7 +276,7 @@ handle_cmd(il_octet_string_t *event, long offset)
                        if(eq->flushing == 2) {
                                eq->flushing = 0;
                                num_replies++;
-                               result = ((result == 1) || (eq->flush_result < 0))  ? 
+                               result = ((result == 1) || (eq->flush_result < 0))  ?
                                        eq->flush_result : result;
                        }
                        event_queue_cond_unlock(eq);
@@ -305,7 +284,7 @@ handle_cmd(il_octet_string_t *event, long offset)
        }
 
        /* prevent deadlock in next flush */
-       if(pthread_mutex_unlock(&flush_lock) < 0) 
+       if(pthread_mutex_unlock(&flush_lock) < 0)
                abort();
 
 
@@ -320,8 +299,10 @@ handle_cmd(il_octet_string_t *event, long offset)
        }
        if(job_id_s) free(job_id_s);
        result = send_confirmation(receipt, result);
-       if(result <= 0) 
-               il_log(LOG_ERR, "handle_cmd: error sending status: %s\n", error_get_msg());
+       if(result <= 0)
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                "handle_cmd: error sending status: %s", 
+                                error_get_msg());
        return(1);
 
 
@@ -335,10 +316,10 @@ cmd_error:
 #endif /* INTERLOGD_HANDLE_CMD */
 
 
-static 
+static
 int
 handle_msg(il_octet_string_t *event, long offset)
-{ 
+{
        struct server_msg *msg = NULL;
 #if !defined(IL_NOTIFICATIONS)
        struct event_queue *eq_l;
@@ -350,25 +331,31 @@ handle_msg(il_octet_string_t *event, long offset)
 
        /* convert event to message for server */
        if((msg = server_msg_create(event, offset)) == NULL) {
-               il_log(LOG_ERR, "    handle_msg: error parsing event '%s':\n      %s\n", event, error_get_msg());
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                                "    handle_msg: error parsing event '%s': %s", 
+                                event, error_get_msg());
                return(0);
        }
-  
+
        /* sync event store with IPC (if neccessary)
         * This MUST be called before inserting event into output queue! */
-       if((es = event_store_find(msg->job_id_s)) == NULL) 
+       if((es = event_store_find(msg->job_id_s, NULL)) == NULL)
                return(-1);
        msg->es = es;
 
 #ifdef LB_PERF
-       if(nosync) 
+       if(nosync)
                ret = 1;
-       else 
+       else
 #endif
                ret = event_store_sync(es, offset);
+       /* no longer informative:
        il_log(LOG_DEBUG, "  syncing event store at %d with event at %d, result %d\n", es->offset, offset, ret);
+       */
        if(ret < 0) {
-               il_log(LOG_ERR, "    handle_msg: error syncing event store:\n      %s\n", error_get_msg());
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                "    handle_msg: error syncing event store: %s", 
+                                error_get_msg());
                /* XXX should error during event store recovery cause us to drop the message? */
                /* Probably no, because the attempt to recover means we have missed some events,
                   and delivery of this one will not move offset ahead. So try our best and deliver it
@@ -391,8 +378,10 @@ handle_msg(il_octet_string_t *event, long offset)
 #else
        eq_s = queue_list_get(msg->job_id_s);
 #endif
-       if(eq_s == NULL) { 
-               il_log(LOG_ERR, "    handle_msg: apropriate queue not found: %s\n", error_get_msg());
+       if(eq_s == NULL) {
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                "    handle_msg: apropriate queue not found: %s", 
+                                error_get_msg());
                clear_error();
        } else {
                if(enqueue_msg(eq_s, msg) < 0)
@@ -426,29 +415,31 @@ err:
 
 
 
-int 
+int
 loop()
 {
        /* receive events */
        while(1) {
-               il_octet_string_t msg;
+               il_octet_string_t *msg;
                long offset;
                int ret;
-    
+
+               do_handle_signal();
                if(killflg)
-                       exit(0);
+                       return (0);
 
                clear_error();
-               if((ret = input_queue_get(&msg, &offset, INPUT_TIMEOUT)) < 0) 
+               if((ret = input_queue_get(&msg, &offset, INPUT_TIMEOUT)) < 0)
                {
                        if(error_get_maj() == IL_PROTO) {
-                               il_log(LOG_DEBUG, "  premature EOF while receiving event\n");
+                               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                                "  premature EOF while receiving event");
                                /* problems with socket input, try to catch up from files */
 #ifndef PERF_EMPTY
                                event_store_recover_all();
 #endif
                                continue;
-                       } else 
+                       } else
                                return(-1);
                }
                else if(ret == 0) {
@@ -456,25 +447,27 @@ loop()
                }
 
 #ifdef PERF_EMPTY
-               glite_wll_perftest_consumeEventString(msg.data);
-               free(msg.data);
+               glite_wll_perftest_consumeEventString(msg->data);
+               free(msg->data);
                continue;
 #endif
 
-#ifdef INTERLOGD_HANDLE_CMD            
-               ret = handle_cmd(&msg, offset);
+#ifdef INTERLOGD_HANDLE_CMD
+               ret = handle_cmd(msg, offset);
                if(ret == 0)
 #endif
-                       ret = handle_msg(&msg, offset);
-               free(msg.data);
+                       ret = handle_msg(msg, offset);
+               if(msg->data) free(msg->data);
                if(ret < 0)
                        switch (error_get_maj()) {
                                case IL_SYS:
                                case IL_NOMEM:
                                        return (ret);
                                        break;
-                               default: 
-                                       il_log(LOG_ERR, "Error: %s\n", error_get_msg());
+                               default:
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                                        "Error: %s", 
+                                                        error_get_msg());
                                        break;
                        }
        } /* while */
index bf3c7a2..ffcca81 100644 (file)
@@ -1,4 +1,21 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -36,7 +53,9 @@ input_queue_attach()
   if(connect(sock, (struct sockaddr *)&saddr, sizeof(saddr.sun_path)) < 0) {
          if(errno == ECONNREFUSED) {
                  /* socket present, but no one at the other end; remove it */
-                 il_log(LOG_WARNING, "  removing stale input socket %s\n", socket_path);
+                 glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, 
+                                  "  removing stale input socket %s", 
+                                  socket_path);
                  unlink(socket_path);
          }
          /* ignore other errors for now */
@@ -76,7 +95,6 @@ read_event(int sock, long *offset, il_octet_string_t *msg)
 {
   char *buffer, *p, *n;
   int  len, alen, i, chunk_size = DEFAULT_CHUNK_SIZE;
-  static char buf[1024];
 
   msg->data = NULL;
   msg->len = 0;
@@ -109,7 +127,6 @@ read_event(int sock, long *offset, il_octet_string_t *msg)
         (alen - (p - buffer)) is the free space,
   */ 
  
-#if 1
   /* Reading events - optimized version. Attempts to increase chunks read by recv
    * when there are more data, reads directly into destination memory (instead of 
    * copying from static buffer) etc.
@@ -153,55 +170,6 @@ read_event(int sock, long *offset, il_octet_string_t *msg)
          }
   } while ( (len > 0) && (n == NULL) );
 
-#else
-  /* Reading events - original version.
-   * Appears to behave quite good, anyway.
-   */
-  while((len=recv(sock, buf, sizeof(buf), MSG_PEEK | MSG_NOSIGNAL)) > 0) {
-
-    /* we have to be prepared for sizeof(buf) bytes */
-    if(alen - (p - buffer) < (int)sizeof(buf)) {
-      alen += 8192;
-      n = realloc(buffer, alen);
-      if(n == NULL) {
-       free(buffer);
-       set_error(IL_NOMEM, ENOMEM, "read_event: no room for event");
-       return(-1);
-      }
-      p = p - buffer + n;
-      buffer = n;
-    }
-
-    /* copy all relevant bytes from buffer */
-    n = (char*)memccpy(p, buf, EVENT_SEPARATOR, len);
-    if(n) {
-           /* separator found */
-           n--; /* but do not preserve it */
-           i = n - p;
-           p = n;
-    } else {
-           /* separator not found */
-           i = len;
-           p += len;
-    }
-   /* This was definitely slowing us down:
-    *    for(i=0; (i < len) && (buf[i] != EVENT_SEPARATOR); i++) 
-    *    *p++ = buf[i];
-    */
-
-    /* remove the data from queue */
-    if(i > 0) 
-      if(recv(sock, buf, i, MSG_NOSIGNAL) != i) {
-       set_error(IL_SYS, errno, "read_event: error reading data");
-       free(buffer);
-       return(-1);
-      }
-    if(i < len)
-      /* the event is complete */
-      break;
-  }
-#endif
-
   /* terminate buffer */
   *p = 0;
 
@@ -243,14 +211,19 @@ read_event(int sock, long *offset, il_octet_string_t *msg)
  */
 #ifdef PERF_EVENTS_INLINE
 int
-input_queue_get(il_octet_string *buffer, long *offset, int timeout)
+input_queue_get(il_octet_string **buffer, long *offset, int timeout)
 {
        static long o = 0;
        int len;
        char *jobid;
+       static il_octet_string_t my_buffer;
 
-       len = glite_wll_perftest_produceEventString(&buffer->data, &jobid);
-       buffer->len = len;
+       assert(buffer != NULL);
+
+       *buffer = &my_buffer;
+
+       len = glite_wll_perftest_produceEventString(&my_buffer.data, &jobid);
+       my_buffer.len = len;
        if(len) {
                o += len;
                *offset = o;
@@ -261,14 +234,17 @@ input_queue_get(il_octet_string *buffer, long *offset, int timeout)
 }
 #else
 int
-input_queue_get(il_octet_string_t *buffer, long *offset, int timeout)
+input_queue_get(il_octet_string_t **buffer, long *offset, int timeout)
 {
   fd_set fds;
   struct timeval tv;
   int msg_len;
+  static il_octet_string_t my_buffer;
 
   assert(buffer != NULL);
 
+  *buffer = &my_buffer;
+
   FD_ZERO(&fds);
   FD_SET(sock, &fds);
   
@@ -284,7 +260,8 @@ input_queue_get(il_octet_string_t *buffer, long *offset, int timeout)
   case -1: /* error */
          switch(errno) {
          case EINTR:
-                 il_log(LOG_DEBUG, "  interrupted while waiting for event!\n");
+                 glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                                  "  interrupted while waiting for event!");
                  return(0);
 
          default:
@@ -300,16 +277,16 @@ input_queue_get(il_octet_string_t *buffer, long *offset, int timeout)
     return(-1);
   }
 
-  read_event(accepted, offset, buffer);
+  read_event(accepted, offset, &my_buffer);
   close(accepted);
 
-  if(buffer->data == NULL) {
+  if(my_buffer.data == NULL) {
     if(error_get_maj() != IL_OK)
       return(-1);
     else
       return(0);
   }
     
-  return(buffer->len);
+  return(my_buffer.len);
 }
 #endif
diff --git a/org.glite.lb.logger/src/input_queue_socket_http.c b/org.glite.lb.logger/src/input_queue_socket_http.c
new file mode 100644 (file)
index 0000000..fd0e303
--- /dev/null
@@ -0,0 +1,184 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "interlogd.h"
+
+static const int   SOCK_QUEUE_MAX = 50;
+extern char *socket_path;
+extern char *file_prefix;
+
+static int sock;
+static int accepted;
+
+static
+int plain_reader(void *user_data, char *buffer, const int len)
+{
+       return (recv(*(int*)user_data, buffer, len, MSG_NOSIGNAL));
+}
+
+                
+int 
+input_queue_attach()
+{ 
+  struct sockaddr_un saddr;
+
+  if((sock=socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
+    set_error(IL_SYS, errno, "input_queue_attach: error creating socket");
+    return(-1);
+  }
+
+  memset(&saddr, 0, sizeof(saddr));
+  saddr.sun_family = AF_UNIX;
+  strcpy(saddr.sun_path, socket_path);
+
+  /* test for the presence of the socket and another instance 
+     of interlogger listening */
+  if(connect(sock, (struct sockaddr *)&saddr, sizeof(saddr.sun_path)) < 0) {
+         if(errno == ECONNREFUSED) {
+                 /* socket present, but no one at the other end; remove it */
+                 il_log(LOG_WARNING, "  removing stale input socket %s\n", socket_path);
+                 unlink(socket_path);
+         }
+         /* ignore other errors for now */
+  } else {
+         /* connection was successful, so bail out - there is 
+            another interlogger running */
+         set_error(IL_SYS, EADDRINUSE, "input_queue_attach: another instance of interlogger is running");
+         return(-1);
+  }
+  
+  if(bind(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+    set_error(IL_SYS, errno, "input_queue_attach: error binding socket");
+    return(-1);
+  }
+
+  if (listen(sock, SOCK_QUEUE_MAX)) {
+    set_error(IL_SYS, errno, "input_queue_attach: error listening on socket");
+    return -1;
+  }
+
+  return(0);
+}
+
+
+void input_queue_detach()
+{
+  if (sock >= 0)
+    close(sock);
+  unlink(socket_path);
+}
+
+
+
+/*
+ * Returns: -1 on error, 0 if no message available, message length otherwise
+ *
+ */
+#ifdef PERF_EVENTS_INLINE
+int
+input_queue_get(il_octet_string_t **buffer, long *offset, int timeout)
+{
+       static long o = 0;
+       int len;
+       char *jobid;
+       static il_octet_string_t my_buffer;
+       
+       assert(buffer != NULL);
+
+       *buffer = &my_buffer;
+
+       len = glite_wll_perftest_produceEventString(&my_buffer.data, &jobid);
+       my_buffer.len = len;
+       if(len) {
+               o += len;
+               *offset = o;
+       } else if (len == 0) {
+               sleep(timeout);
+       }
+       return(len);
+}
+#else
+int
+input_queue_get(il_octet_string_t **buffer, long *offset, int timeout)
+{
+  fd_set fds;
+  struct timeval tv;
+  int msg_len;
+  static il_http_message_t msg;
+
+  assert(buffer != NULL);
+
+  *buffer = (il_octet_string_t *)&msg;
+
+  FD_ZERO(&fds);
+  FD_SET(sock, &fds);
+  
+  tv.tv_sec = timeout;
+  tv.tv_usec = 0;
+  
+  msg_len = select(sock + 1, &fds, NULL, NULL, timeout >= 0 ? &tv : NULL);
+  switch(msg_len) {
+     
+  case 0: /* timeout */
+    return(0);
+    
+  case -1: /* error */
+         switch(errno) {
+         case EINTR:
+                 il_log(LOG_DEBUG, "  interrupted while waiting for event!\n");
+                 return(0);
+
+         default:
+                 set_error(IL_SYS, errno, "input_queue_get: error waiting for event");
+                 return(-1);
+         }
+  default:
+         break;
+  }
+  
+  if((accepted=accept(sock, NULL, NULL)) < 0) {
+    set_error(IL_SYS, errno, "input_queue_get: error accepting connection");
+    return(-1);
+  }
+
+  msg_len = receive_http(&accepted, plain_reader, &msg);
+
+  if(msg_len < 0) {
+         close(accepted);
+         if(error_get_maj() != IL_OK) 
+                 return -1;
+         else
+                 return 0;
+  }
+
+  close(accepted);
+  *offset = -1;
+  return(msg.len);
+}
+#endif
+
index a66d7f4..f8002ec 100644 (file)
@@ -1,18 +1,38 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 /*
    interlogger - collect events from local-logger and send them to logging and bookkeeping servers
 
 */
+#include <stdio.h>
 #include <getopt.h>
 #include <string.h>
 #include <signal.h>
 #include <pthread.h>
-
-#include <globus_common.h>
+#include <unistd.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/stat.h>
+#include <sys/param.h>
 
 #include "interlogd.h"
-#include "glite/lb/consumer.h"
 #include "glite/lb/log_proto.h"
 #include "glite/security/glite_gss.h"
 #ifdef LB_PERF
 #if defined(IL_NOTIFICATIONS)
 #define DEFAULT_PREFIX "/tmp/notif_events"
 #define DEFAULT_SOCKET "/tmp/notif_interlogger.sock"
+#define DEFAULT_PIDFILE "/var/glite/glite-lb-notif-interlogd.pid"
 #else
 #define DEFAULT_PREFIX EDG_WLL_LOG_PREFIX_DEFAULT
 #define DEFAULT_SOCKET "/tmp/interlogger.sock"
+#define DEFAULT_PIDFILE  "/var/glite/glite-lb-interlogd.pid"
 #endif
 
 
+
 /* The name the program was run with, stripped of any leading path. */
 char *program_name;
 int killflg = 0;
 
 int TIMEOUT = DEFAULT_TIMEOUT;
 
-gss_cred_id_t cred_handle = GSS_C_NO_CREDENTIAL;
+cred_handle_t *cred_handle = NULL;
 pthread_mutex_t cred_handle_lock = PTHREAD_MUTEX_INITIALIZER;
 
 time_t key_mtime = 0, cert_mtime = 0;
 
+static char *pidfile = DEFAULT_PIDFILE;
+
 static void usage (int status)
 {
        printf("%s - \n"
@@ -55,9 +80,14 @@ static void usage (int status)
               "  -k, --key  <file>          location of server private key\n"
               "  -C, --CAdir <dir>          directory containing CA certificates\n"
               "  -b, --book                 send events to bookkeeping server only\n"
+              "  -i, --pidfile                  pid file\n"
               "  -l, --log-server <host>    specify address of log server\n"
               "  -s, --socket <path>        non-default path of local socket\n"
               "  -L, --lazy [<timeout>]     be lazy when closing connections to servers (default, timeout==0 means turn lazy off)\n"
+              "  -p, --parallel [<num>]     use <num> parallel streams to the same server\n"
+              "  -q, --queue-low <num>      queue length that enables another insertions\n"
+              "  -Q, --queue-high <num>     max queue length\n"
+                  "  -F, --conf <file>                  load configuration from config file\n"
 #ifdef LB_PERF
               "  -n, --nosend               PERFTEST: consume events instead of sending\n"
               "  -S, --nosync               PERFTEST: do not check logd files for lost events\n"
@@ -80,6 +110,10 @@ char *file_prefix = DEFAULT_PREFIX;
 int bs_only = 0;
 int lazy_close = 1;
 int default_close_timeout;
+size_t max_store_size;
+size_t queue_size_low = 0;
+size_t queue_size_high = 0;
+int parallel = 0;
 #ifdef LB_PERF
 int nosend = 0, norecover=0, nosync=0, noparse=0;
 char *event_source = NULL;
@@ -91,6 +125,8 @@ char *key_file  = NULL;
 char *CAcert_dir = NULL;
 char *log_server = NULL;
 char *socket_path = DEFAULT_SOCKET;
+static char *conf_file = NULL;
+static char *config = NULL;
 
 static struct option const long_options[] =
 {
@@ -103,9 +139,15 @@ static struct option const long_options[] =
   {"key", required_argument, 0, 'k'},
   {"book", no_argument, 0, 'b'},
   {"CAdir", required_argument, 0, 'C'},
+  {"pidfile", required_argument, 0, 'i'},
   {"log-server", required_argument, 0, 'l'},
   {"socket", required_argument, 0, 's'},
   {"lazy", optional_argument, 0, 'L'},
+  {"max-store", required_argument, 0, 'M'},
+  {"parallel", optional_argument, 0, 'p'},
+  {"queue_size_low", required_argument, 0, 'q'},
+  {"queue_size_high", required_argument, 0, 'Q'},
+  {"conf", required_argument, 0, 'F'},
 #ifdef LB_PERF
   {"nosend", no_argument, 0, 'n'},
   {"nosync", no_argument, 0, 'S'},
@@ -139,8 +181,13 @@ decode_switches (int argc, char **argv)
                           "k:"          /* key */
                           "C:"          /* CA dir */
                           "b"  /* only bookeeping */
-                           "l:" /* log server */
+                          "i:"  /* pidfile*/
+               "l:" /* log server */
                           "d" /* debug */
+                          "p" /* parallel */
+                          "q:"
+                          "Q:"
+                          "F:" /* conf file */
 #ifdef LB_PERF
                           "n" /* nosend */
                           "S" /* nosync */
@@ -150,9 +197,10 @@ decode_switches (int argc, char **argv)
                           "e:" /* event file */
                           "j:" /* num jobs */
 #endif
-#endif                    
+#endif
                           "L::" /* lazy */
-                          "s:", /* socket */
+                          "s:" /* socket */
+                          "M:" /* max-store */,
                           long_options, (int *) 0)) != EOF)
     {
       switch (c)
@@ -192,6 +240,10 @@ decode_switches (int argc, char **argv)
          log_server = strdup(optarg);
          break;
 
+       case 'i': 
+         pidfile = strdup(optarg);
+         break;
+
        case 'C':
          CAcert_dir = strdup(optarg);
          break;
@@ -202,7 +254,7 @@ decode_switches (int argc, char **argv)
 
        case 'L':
                lazy_close = 1;
-               if(optarg) 
+               if(optarg)
                        default_close_timeout = atoi(optarg);
                        if(default_close_timeout == 0) {
                                default_close_timeout = TIMEOUT;
@@ -212,6 +264,29 @@ decode_switches (int argc, char **argv)
                        default_close_timeout = TIMEOUT;
                break;
 
+       case 'M':
+               max_store_size = atoi(optarg);
+               break;
+
+       case 'p':
+               if(optarg)
+                       parallel = atoi(optarg);
+               else
+                       parallel = 4;
+               break;
+
+       case 'q':
+               queue_size_low = atoi(optarg);
+               break;
+
+       case 'Q':
+               queue_size_high = atoi(optarg);
+               break;
+
+       case 'F':
+               conf_file = strdup(optarg);
+               break;
+
 #ifdef LB_PERF
        case 'n':
                nosend = 1;
@@ -249,11 +324,96 @@ decode_switches (int argc, char **argv)
 }
 
 
-void handle_signal(int num) {
-    il_log(LOG_DEBUG, "Received signal %d\n", num);
-    killflg++;
+char *load_conf_file(char *filename)
+{
+       struct stat fs;
+       FILE *cf;
+       char *s;
+
+       if(stat(filename, &fs) < 0) {
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR,
+                               "Could not stat config file %s: %s\n", filename, strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       s = malloc(fs.st_size + 1);
+       if(s == NULL) {
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR, "Not enough memory for config file");
+               exit(EXIT_FAILURE);
+       }
+       cf = fopen(filename, "r");
+       if(cf == NULL) {
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR,
+                               "Error opening config file %s: %s\n", filename, strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       if(fread(s, fs.st_size, 1, cf) != 1) {
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR,
+                               "Error reading config file %s: %s\n", filename, strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       fclose(cf);
+       s[fs.st_size] = 0;
+       return s;
+}
+
+static int     received_signal = 0;
+
+static void handle_signal(int num)
+{
+       received_signal = num;
 }
 
+
+void do_handle_signal() {
+
+       if (received_signal == 0) return;
+
+       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Received signal %d\n", received_signal);
+
+       switch(received_signal) {
+       case SIGHUP:
+               /* TODO: reload all external configurations, see
+               https://rt3.cesnet.cz/rt/Ticket/Display.html?id=24879 */
+               glite_common_log_reread();
+               break;
+
+       case SIGUSR1:
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO,
+                                "Logging priority is now %s for %s, %s for %s and %s for %s\n", 
+                                glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_SECURITY)),
+                                LOG_CATEGORY_SECURITY,
+                                glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_ACCESS)),
+                                LOG_CATEGORY_ACCESS,
+                                glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_CONTROL)),
+                                LOG_CATEGORY_CONTROL);
+               break;
+
+       case SIGUSR2:
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO,
+                                "Logging priority is now %s for %s and %s for %s\n", 
+                                glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_LB)),
+                                LOG_CATEGORY_LB,
+                                glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_LB_IL)),
+                                IL_LOG_CATEGORY);
+               break;
+
+       case SIGPIPE:
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Broken pipe, lost communication channel.\n");
+               break;
+
+       case SIGINT:
+       case SIGTERM:
+       case SIGQUIT:
+               killflg++;
+               break;
+
+       }
+
+       received_signal = 0;
+}
+
+
+
 int
 main (int argc, char **argv)
 {
@@ -261,6 +421,7 @@ main (int argc, char **argv)
   char *p;
   edg_wll_GssStatus gss_stat;
   int ret;
+  FILE *pidf;
 
   program_name = argv[0];
 
@@ -271,31 +432,62 @@ main (int argc, char **argv)
 
   i = decode_switches (argc, argv);
 
+  if(glite_common_log_init()) {
+         fprintf(stderr, "glite_common_log_init() failed, exiting.\n");
+         exit(EXIT_FAILURE);
+  }
+
+  /* parse config file, if any */
+  if(conf_file != NULL) {
+         config = load_conf_file(conf_file);
+  }
+
+  /* check for reasonable queue lengths */
+  if((queue_size_low == 0 && queue_size_high > 0) ||
+     (queue_size_low > queue_size_high)) {
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "max queue length -Q must be greater than low queue length -q, both or none must be specified!");
+         exit(EXIT_FAILURE);
+  }
+
   /* force -b if we do not have log server */
   if(log_server == NULL) {
     log_server = strdup(DEFAULT_LOG_SERVER);
     bs_only = 1;
   }
 
-  if(init_errors(verbose ? LOG_DEBUG : LOG_WARNING)) {
-    fprintf(stderr, "Failed to initialize error message subsys. Exiting.\n");
-    exit(EXIT_FAILURE);
+  /* initialize error reporting */
+  if(init_errors()) {
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to initialize error message subsystem. Exiting.");
+         exit(EXIT_FAILURE);
   }
 
   if (signal(SIGPIPE, SIG_IGN) == SIG_ERR
       || signal(SIGABRT, handle_signal) == SIG_ERR
       || signal(SIGTERM, handle_signal) == SIG_ERR
       || signal(SIGINT, handle_signal) == SIG_ERR) {
-    perror("signal");
-    exit(EXIT_FAILURE);
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to setup signal handlers: %s, exiting.",
+                          strerror(errno));
+         exit(EXIT_FAILURE);
+  }
+
+/* just try it before deamonizing to be able to complain aloud */
+  if (!(pidf = fopen(pidfile,"w"))) {
+       perror(pidfile);
+       exit(EXIT_FAILURE);
   }
+  fclose(pidf);
 
   if(!debug &&
      (daemon(0,0) < 0)) {
-    perror("daemon");
-    exit(EXIT_FAILURE);
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to daemonize itself: %s, exiting.",
+                          strerror(errno));
+         exit(EXIT_FAILURE);
   }
 
+  pidf = fopen(pidfile,"w"); assert(pidf); /* XXX */
+  fprintf(pidf,"%d\n",getpid());
+  fclose(pidf);
+
 #ifdef LB_PERF
   /* this must be called after installing signal handlers */
   glite_wll_perftest_init(NULL, /* host */
@@ -305,45 +497,78 @@ main (int argc, char **argv)
                          njobs);
 #endif
 
-  il_log(LOG_INFO, "Initializing input queue...\n");
   if(input_queue_attach() < 0) {
-    il_log(LOG_CRIT, "Failed to initialize input queue: %s\n", error_get_msg());
-    exit(EXIT_FAILURE);
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to initialize input queue: %s", 
+                          error_get_msg());
+         exit(EXIT_FAILURE);
   }
+  glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Initialized input queue.");
 
   /* initialize output queues */
-  il_log(LOG_INFO, "Initializing event queues...\n");
   if(queue_list_init(log_server) < 0) {
-    il_log(LOG_CRIT, "Failed to initialize output event queues: %s\n", error_get_msg());
-    exit(EXIT_FAILURE);
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to initialize output event queues: %s", 
+                          error_get_msg());
+         exit(EXIT_FAILURE);
   }
+  glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Initialized event queues.");
   if(lazy_close)
-         il_log(LOG_DEBUG, "  using lazy mode when closing connections, timeout %d\n",
-                default_close_timeout);
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "  using lazy mode when closing connections, timeout %d",
+                          default_close_timeout);
 
+  /* get credentials */
   if (CAcert_dir)
      setenv("X509_CERT_DIR", CAcert_dir, 1);
-
   edg_wll_gss_watch_creds(cert_file,&cert_mtime);
-  ret = edg_wll_gss_acquire_cred_gsi(cert_file, key_file, &cred_handle, NULL, &gss_stat);
+  cred_handle = malloc(sizeof(*cred_handle));
+  if(cred_handle == NULL) {
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to allocate structure for credentials.");
+         exit(EXIT_FAILURE);
+  }
+  cred_handle->creds = NULL;
+  cred_handle->counter = 0;
+  ret = edg_wll_gss_acquire_cred_gsi(cert_file, key_file, &cred_handle->creds, &gss_stat);
   if (ret) {
      char *gss_err = NULL;
-     char *str;
 
      if (ret == EDG_WLL_GSS_ERROR_GSS)
        edg_wll_gss_get_error(&gss_stat, "edg_wll_gss_acquire_cred_gsi()", &gss_err);
-     asprintf(&str, "Failed to load GSI credential: %s\n",
-             (gss_err) ? gss_err : "edg_wll_gss_acquire_cred_gsi() failed"); 
-     il_log(LOG_CRIT, str);
-     free(str);
+     glite_common_log(LOG_CATEGORY_SECURITY, LOG_PRIORITY_FATAL, "Failed to load GSI credential: %s, exiting.",
+                     (gss_err) ? gss_err : "edg_wll_gss_acquire_cred_gsi() failed");
      if (gss_err)
        free(gss_err);
      exit(EXIT_FAILURE);
   }
-  
-  if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS)  {
-       il_log(LOG_CRIT, "Failed to initialize Globus common module\n");
-       exit(EXIT_FAILURE);
+  glite_common_log(LOG_CATEGORY_SECURITY, LOG_PRIORITY_INFO, "Using certificate %s", cred_handle->creds->name);
+
+  /* parse config, initialize plugins */
+  glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Initializing plugins:\n");
+  if(config) {
+         char *s = strstr(config, "[interlogd]");
+         char *p;
+         char name[MAXPATHLEN+1];
+
+         /* next line */
+         s = strchr(s, '\n');
+         if(s) s++;
+         while(s) {
+                 if(*s == 0 || *s == '[')
+                         break;
+                 /* parse line */
+                 p = strchr(s, '\n');
+                 if(p) {
+                         *p = 0;
+                 }
+                 /* XXX possible overflow by long line in config file */
+                 ret = sscanf(s, " plugin =%s", name);
+                 if(p) *p = '\n';
+                 if(ret > 0) {
+                         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "  loading plugin %s\n", name);
+                         if(plugin_mgr_init(name, config) < 0) {
+                                 glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR, "Failed to load plugin %s: %s\n", name, error_get_msg());
+                         }
+                 }
+                 s = p + 1;
+         }
   }
 
 #ifndef PERF_EMPTY
@@ -351,35 +576,38 @@ main (int argc, char **argv)
 #ifdef LB_PERF
   if(norecover) {
          if(event_store_init(file_prefix) < 0) {
-                 il_log(LOG_CRIT, "Failed to init event stores: %s\n", error_get_msg());
+                 glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to initialize event stores: %s", 
+                                  error_get_msg());
                  exit(EXIT_FAILURE);
          }
   } else
 #endif
-  { 
+  {
          pthread_t rid;
 
-         il_log(LOG_INFO, "Starting recovery thread...\n");
          if(pthread_create(&rid, NULL, recover_thread, NULL) < 0) {
-                 il_log(LOG_CRIT, "Failed to start recovery thread: %s\n", strerror(errno));
+                 glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Failed to start recovery thread: %s", strerror(errno));
                  exit(EXIT_FAILURE);
          }
          pthread_detach(rid);
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Started recovery thread.");
   }
 #endif
 
-  il_log(LOG_INFO, "Entering main loop...\n");
+  glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Entering main loop.");
 
   /* do the work */
   if(loop() < 0) {
-    il_log(LOG_CRIT, "Fatal error: %s\n", error_get_msg());
-    if (killflg) {
-      input_queue_detach();
-      exit(EXIT_FAILURE);
-    }
+         glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "Fatal error: %s", error_get_msg());
+         if (killflg) {
+                 input_queue_detach();
+                 unlink(pidfile);
+                 exit(EXIT_FAILURE);
+         }
   }
-  il_log(LOG_INFO, "Done!\n");
+  glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, "Done!");
   input_queue_detach();
+  unlink(pidfile);
 
   exit (0);
 }
index 36489db..d2aaf76 100644 (file)
@@ -1,32 +1,61 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
+#include <stdio.h>
 #include <sys/types.h>
 #include <sys/time.h>
 #include <sys/resource.h>
 #include <sys/wait.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
+#include <netinet/tcp.h>
 #include <arpa/inet.h>
 #include <signal.h>
 #include <unistd.h> 
 #include <string.h>
 #include <getopt.h>
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
 
-#include <globus_common.h>
+#if defined(FREEBSD) || defined(__FreeBSD__)
+#define TCP_CORK TCP_NOPUSH
+#endif
 
+#include "glite/lbu/log.h"
 #include "glite/lb/context-int.h"
+#include "glite/lb/timeouts.h"
 #include "logd_proto.h"
-#include "glite/lb/consumer.h"
 #include "glite/security/glite_gss.h"
 #ifdef LB_PERF
 #include "glite/lb/lb_perftest.h"
 #endif
 
+#define DEFAULT_PIDFILE "/var/glite/glite-lb-logd.pid"
+
+typedef void (*logd_handler_t)(int);
+
 static const char rcsid[] = "@(#)$Id$";
-static int verbose = 0;
 static int debug = 0;
 static int port = EDG_WLL_LOG_PORT_DEFAULT;
 static char *prefix = EDG_WLL_LOG_PREFIX_DEFAULT;
+static char *pidfile = DEFAULT_PIDFILE;
 static char *cert_file = NULL;
 static char *key_file = NULL;
 static char *CAcert_dir = NULL;
@@ -43,13 +72,13 @@ extern char confirm_sock_name[256];
 static struct option const long_options[] = {
        { "help", no_argument, 0, 'h' },
        { "version", no_argument, 0, 'V' },
-       { "verbose", no_argument, 0, 'v' },
        { "debug", no_argument, 0, 'd' },
        { "port", required_argument, 0, 'p' },
        { "file-prefix", required_argument, 0, 'f' },
        { "cert", required_argument, 0, 'c' },
        { "key", required_argument, 0, 'k' },
        { "CAdir", required_argument, 0, 'C' },
+       { "pidfile",required_argument, 0, 'i' },
        { "socket",required_argument, 0, 's' },
        { "noAuth", no_argument, 0, 'x' },
        { "noIPC", no_argument, 0, 'y' },
@@ -75,26 +104,25 @@ usage(char *program_name) {
                "-h, --help                 display this help and exit\n"
                "-V, --version              output version information and exit\n"
                "-d, --debug                do not run as daemon\n"
-               "-v, --verbose              print extensive debug output\n"
                "-p, --port <num>           port to listen\n"
                "-f, --file-prefix <prefix> path and prefix for event files\n"
                "-c, --cert <file>          location of server certificate\n"
                "-k, --key  <file>          location of server private key\n"
                "-C, --CAdir <dir>          directory containing CA certificates\n"
                "-s, --socket <dir>         interlogger's socket to send messages to\n"
+               "-i, --pidfile <file>       pid file\n"
                "--noAuth                   do not check caller's identity\n"
                "--noIPC                    do not send messages to inter-logger\n"
                "--noParse                  do not parse messages for correctness\n",
                program_name,program_name);
 }
 
-static sighandler_t mysignal(int num,sighandler_t handler)
+static logd_handler_t mysignal(int num,logd_handler_t handler)
 {
        struct sigaction        sa,osa;
 
        memset(&sa,0,sizeof(sa));
        sa.sa_handler = handler;
-       sa.sa_flags = SA_RESTART;
        return sigaction(num,&sa,&osa) ? SIG_ERR : osa.sa_handler;
 }
 
@@ -102,40 +130,70 @@ static sighandler_t mysignal(int num,sighandler_t handler)
  *----------------------------------------------------------------------
  *
  * handle_signal -
- *     USR1 - increase the verbosity of the program
- *     USR2 - decrease the verbosity of the program
+ *      HUP  - reread log4crc
+ *     USR1 - print priorities of all standard categories
+ *     USR2 - print priorities of all LB categories
  *
  *----------------------------------------------------------------------
  */
-void handle_signal(int num) {
-       if (num != SIGCHLD) edg_wll_ll_log(LOG_NOTICE,"Received signal %d\n", num);
-       switch (num) {
-               case SIGUSR1:
-                       if (edg_wll_ll_log_level < LOG_DEBUG) edg_wll_ll_log_level++;
-                       edg_wll_ll_log(LOG_NOTICE,"Logging level is now %d\n", edg_wll_ll_log_level);
-                       break;
-               case SIGUSR2:
-                       if (edg_wll_ll_log_level > LOG_EMERG) edg_wll_ll_log_level--;
-                       edg_wll_ll_log(LOG_NOTICE,"Logging level is now %d\n", edg_wll_ll_log_level);
-                       break;
-               case SIGPIPE:
-                       edg_wll_ll_log(LOG_NOTICE,"Broken pipe, lost communication channel.\n");
-                       break;
-               case SIGCHLD:
-                       while (wait3(NULL,WNOHANG,NULL) > 0);
-                       break;
-               case SIGINT:
-               case SIGTERM:
-               case SIGQUIT:
-                       if (confirm_sock) {
-                               edg_wll_ll_log(LOG_NOTICE,"Closing confirmation socket.\n");
-                               close(confirm_sock);
-                               unlink(confirm_sock_name);
-                       }
-                       exit(1);
-                       break;
-               default: break;
+
+static int received_signal = 0;
+
+static void handle_signal(int num)
+{
+       received_signal = num;
+}
+
+void do_handle_signal() {
+
+       if (received_signal == 0) return;
+       
+       if (received_signal != SIGCHLD) glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Received signal %d\n", received_signal);
+       switch (received_signal) {
+       case SIGHUP:
+               /* TODO: reload all external configurations, see
+               https://rt3.cesnet.cz/rt/Ticket/Display.html?id=24879 */
+               glite_common_log_reread();
+               break;
+       case SIGUSR1:
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,
+                       "Logging priority is now %s for %s, %s for %s and %s for %s\n", 
+                       glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_SECURITY)),
+                       LOG_CATEGORY_SECURITY,
+                       glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_ACCESS)),
+                       LOG_CATEGORY_ACCESS,
+                       glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_CONTROL)),
+                       LOG_CATEGORY_CONTROL);
+               break;
+       case SIGUSR2:
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,
+                       "Logging priority is now %s for %s and %s for %s\n", 
+                       glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_LB)),
+                       LOG_CATEGORY_LB,
+                       glite_common_log_priority_to_string(glite_common_log_get_priority(LOG_CATEGORY_LB_LOGD)),
+                       LOG_CATEGORY_LB_LOGD);
+               break;
+       case SIGPIPE:
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Broken pipe, lost communication channel.\n");
+               break;
+       case SIGCHLD:
+               while (wait3(NULL,WNOHANG,NULL) > 0);
+               break;
+       case SIGINT:
+       case SIGTERM:
+       case SIGQUIT:
+               if (confirm_sock) {
+                       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Closing confirmation socket.\n");
+                       close(confirm_sock);
+                       unlink(confirm_sock_name);
+               }
+               unlink(pidfile);
+               exit(1);
+               break;
+       default: break;
        }
+
+       received_signal = 0;
 }
 
 /*
@@ -146,20 +204,18 @@ void handle_signal(int num) {
  *----------------------------------------------------------------------
  */
 static int
-doit(int socket, gss_cred_id_t cred_handle, char *file_name_prefix, int noipc, int noparse)
+doit(int socket, edg_wll_GssCred cred_handle, char *file_name_prefix, int noipc, int noparse)
 {
     char       *subject;
     int        ret,fd,count;
     struct timeval timeout;
     edg_wll_GssConnection      con;
     edg_wll_GssStatus  gss_stat;
-    gss_buffer_desc    gss_token = GSS_C_EMPTY_BUFFER;
-    gss_name_t client_name = GSS_C_NO_NAME;
-    OM_uint32  min_stat;
-    gss_OID    name_type = GSS_C_NO_OID;
+    edg_wll_GssPrincipal client = NULL;
     fd_set fdset;
-    struct sockaddr_in peer;
+    struct sockaddr_storage    peer;
     socklen_t  alen = sizeof peer;
+    char       peerhost[64], peerserv[16];
 
     ret = count = 0;
     FD_ZERO(&fdset);
@@ -168,45 +224,61 @@ doit(int socket, gss_cred_id_t cred_handle, char *file_name_prefix, int noipc, i
     timeout.tv_sec = ACCEPT_TIMEOUT;
     timeout.tv_usec = 0;
     getpeername(socket,(struct sockaddr *) &peer,&alen);
-    edg_wll_ll_log(LOG_DEBUG,"Accepting connection (remaining timeout %d.%06d sec)\n",
+    glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Accepting connection (remaining timeout %d.%06d sec)\n",
                (int)timeout.tv_sec, (int) timeout.tv_usec);
+    
+    ret = getnameinfo ((struct sockaddr *) &peer, alen, 
+               peerhost, sizeof(peerhost), peerserv, sizeof(peerserv), NI_NUMERICHOST | NI_NUMERICSERV);
+    if (ret) {
+       glite_common_log(LOG_CATEGORY_ACCESS, LOG_PRIORITY_WARN, "getnameinfo: %s", gai_strerror (ret));
+       strcpy(peerhost, "unknown"); strcpy(peerserv, "unknown"); 
+    }
+
+/* XXX: ugly workaround, we may detect false expired certificated
+ * probably due to bug in Globus GSS/SSL. */
+#define _EXPIRED_CERTIFICATE_MESSAGE "certificate has expired"
+
     if ((ret = edg_wll_gss_accept(cred_handle,socket,&timeout,&con, &gss_stat)) < 0) {
-       edg_wll_ll_log(LOG_DEBUG,"timeout after gss_accept is %d.%06d sec\n",
+       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_DEBUG,"timeout after gss_accept is %d.%06d sec\n",
                (int)timeout.tv_sec, (int) timeout.tv_usec);
-        edg_wll_ll_log(LOG_ERR,"%s: edg_wll_gss_accept() failed\n",inet_ntoa(peer.sin_addr));
-       return edg_wll_log_proto_server_failure(ret,&gss_stat,"edg_wll_gss_accept() failed\n");
+       if ( ret == EDG_WLL_GSS_ERROR_TIMEOUT ) {
+               glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: Client authentication failed - timeout reached, closing.\n",peerhost);
+       } else if (ret == EDG_WLL_GSS_ERROR_GSS) {
+               char *gss_err;
+
+               edg_wll_gss_get_error(&gss_stat, "Client authentication failed", &gss_err);
+               if (strstr(gss_err,_EXPIRED_CERTIFICATE_MESSAGE)) {
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: false expired certificate: %s\n",peerhost,gss_err);
+                       free(gss_err);
+                       return -1;
+               }
+               glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: GSS error: %s, closing.\n",peerhost,gss_err);
+               free(gss_err);
+       } else {
+               glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: Client authentication failed, closing.\n",peerhost);
+       }
+       return 1;
     }
 
     /* authenticate */
-    edg_wll_ll_log(LOG_INFO,"Processing authentication:\n");
-    gss_stat.major_status = gss_inquire_context(&gss_stat.minor_status, con.context,
-                                               &client_name, NULL, NULL, NULL, NULL,
-                                               NULL, NULL);
-    if (GSS_ERROR(gss_stat.major_status)) {
-       char *gss_err;
-       edg_wll_gss_get_error(&gss_stat, "Cannot read client identification", &gss_err);
-       edg_wll_ll_log(LOG_WARNING, "%s: %s\n", inet_ntoa(peer.sin_addr),gss_err);
-       free(gss_err);
-    } else {
-       gss_stat.major_status = gss_display_name(&gss_stat.minor_status, client_name,
-                                               &gss_token, &name_type);
-       if (GSS_ERROR(gss_stat.major_status)) {
-         char *gss_err;
-         edg_wll_gss_get_error(&gss_stat, "Cannot process client identification", &gss_err);
-         edg_wll_ll_log(LOG_WARNING, "%s: %s\n",inet_ntoa(peer.sin_addr),gss_err);
-         free(gss_err);
-       }
+    glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_DEBUG,"Processing authentication:\n");
+    ret = edg_wll_gss_get_client_conn(&con, &client, &gss_stat);
+    if (ret) {
+        char *gss_err;
+        edg_wll_gss_get_error(&gss_stat, "Cannot read client identification", &gss_err);
+        glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN, "%s: %s\n", peerhost,gss_err);
+        free(gss_err);
     }
 
-    if (GSS_ERROR(gss_stat.major_status) || edg_wll_gss_oid_equal(name_type, GSS_C_NT_ANONYMOUS)) {
-       edg_wll_ll_log(LOG_INFO,"  User not authenticated, setting as \"%s\". \n",EDG_WLL_LOG_USER_DEFAULT);
+    if (ret || client->flags & EDG_WLL_GSS_FLAG_ANON) {
+       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"  User not authenticated, setting as \"%s\". \n",EDG_WLL_LOG_USER_DEFAULT);
        subject=strdup(EDG_WLL_LOG_USER_DEFAULT);
     } else {
-       edg_wll_ll_log(LOG_INFO,"  User successfully authenticated as:\n");
-       edg_wll_ll_log(LOG_INFO, "   %s\n", (char *)gss_token.value);
-       subject=gss_token.value;
-       memset(&gss_token.value, 0, sizeof(gss_token.value));
+       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_INFO,"  User successfully authenticated as: %s\n",client->name);
+       subject=strdup(client->name);
     }
+    if (client)
+       edg_wll_gss_free_princ(client);
 
     /* get and process the data */
     timeout.tv_sec = CONNECTION_TIMEOUT;
@@ -214,41 +286,42 @@ doit(int socket, gss_cred_id_t cred_handle, char *file_name_prefix, int noipc, i
     
     while (timeout.tv_sec > 0) {
        count++;
-       edg_wll_ll_log(LOG_DEBUG,"Waiting for data delivery no. %d (remaining timeout %d.%06d sec)\n",
+       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Waiting for data delivery no. %d (remaining timeout %d.%06d sec)\n",
                count, (int)timeout.tv_sec, (int) timeout.tv_usec);
        FD_SET(con.sock,&fdset);
        fd = select(con.sock+1,&fdset,NULL,NULL,&timeout);
        switch (fd) {
        case 0: /* timeout */
-               edg_wll_ll_log(LOG_DEBUG,"Connection timeout expired\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Connection timeout expired\n");
                timeout.tv_sec = 0; 
                break;
        case -1: /* error */
                switch(errno) {
                case EINTR:
-                       edg_wll_ll_log(LOG_DEBUG,"XXX: Waking up (remaining timeout %d.%06d sec)\n",
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"XXX: Waking up (remaining timeout %d.%06d sec)\n",
                                (int)timeout.tv_sec, (int) timeout.tv_usec);
                        continue;
                default:
-                       SYSTEM_ERROR("select");
+                       glite_common_log_SYS_ERROR("select");
                        timeout.tv_sec = 0;
                        break;
                }
                break;
        default:
-               edg_wll_ll_log(LOG_DEBUG,"Waking up (remaining timeout %d.%06d sec)\n",
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Waking up (remaining timeout %d.%06d sec)\n",
                        (int)timeout.tv_sec, (int) timeout.tv_usec);
                break;
        }
        if (FD_ISSET(con.sock,&fdset)) {
                ret = edg_wll_log_proto_server(&con,&timeout,subject,file_name_prefix,noipc,noparse);
+               // TODO: put into edg_wll_log_proto_server?
                if (ret != 0) {
-                       edg_wll_ll_log(LOG_DEBUG,"timeout after edg_wll_log_proto_server is %d.%06d sec\n",
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"timeout after edg_wll_log_proto_server is %d.%06d sec\n",
                                (int)timeout.tv_sec, (int) timeout.tv_usec);
                        if (ret != EDG_WLL_GSS_ERROR_EOF) 
-                               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): Error\n");
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): Error\n");
                        else if (count == 1)
-                               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): Error. EOF occured.\n");
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): Error. EOF occured.\n");
                        timeout.tv_sec = 0;
                        timeout.tv_usec = 0;
                        break;
@@ -260,16 +333,9 @@ doit(int socket, gss_cred_id_t cred_handle, char *file_name_prefix, int noipc, i
 
     }
 
-doit_end:
-       edg_wll_ll_log(LOG_DEBUG, "Closing descriptor '%d'...",con.sock);
+       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG, "Closing descriptor %d.",con.sock);
        edg_wll_gss_close(&con, NULL);
-       if (con.sock == -1) 
-               edg_wll_ll_log(LOG_DEBUG, "o.k.\n");
        if (subject) free(subject);
-       if (gss_token.length)
-               gss_release_buffer(&min_stat, &gss_token);
-       if (client_name != GSS_C_NO_NAME)
-               gss_release_name(&min_stat, &client_name);
        return ret;
 }
 
@@ -285,18 +351,16 @@ int main(int argc, char *argv[])
    int ret;
    int childpid;
    int opt;
+   FILE *pidf;
 
    int listener_fd;
    int client_fd;
-   struct sockaddr_in client_addr;
-   int client_addr_len;
+   struct sockaddr_storage client_addr;
+   socklen_t client_addr_len;
 
-   char *my_subject_name = NULL;
-
-   time_t      cert_mtime = 0, key_mtime = 0;
-   OM_uint32   min_stat;
+   time_t      cert_mtime = 0;
    edg_wll_GssStatus   gss_stat;
-   gss_cred_id_t       cred = GSS_C_NO_CREDENTIAL;
+   edg_wll_GssCred     cred = NULL;
 
 
    setlinebuf(stdout);
@@ -310,7 +374,6 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
    while ((opt = getopt_long(argc,argv,
        "h"  /* help */
        "V"  /* version */
-       "v"  /* verbose */
        "d"  /* debug */
        "p:" /* port */
        "f:" /* file prefix */
@@ -318,6 +381,7 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
        "k:" /* key */
        "C:" /* CA dir */
        "s:" /* socket */
+       "i:" /* pidfile */
        "x"  /* noAuth */
        "y"  /* noIPC */
        "z",  /* noParse */
@@ -325,7 +389,6 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
 
        switch (opt) {
                case 'V': fprintf(stdout,"%s:\t%s\n",argv[0],rcsid); exit(0);
-               case 'v': verbose = 1; break;
                case 'd': debug = 1; break;
                case 'p': port = atoi(optarg); break;
                case 'f': prefix = optarg; break;
@@ -333,6 +396,7 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
                case 'k': key_file = optarg; break;
                case 'C': CAcert_dir = optarg; break;
                case 's': socket_path = optarg; break;
+               case 'i': pidfile = optarg; break;
                case 'x': noAuth = 1; break;
                case 'y': noIPC = 1; break;
                case 'z': noParse = 1; break;
@@ -341,55 +405,42 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
                        usage(argv[0]); exit(0);
        }
    }
-#ifdef LB_PERF
-   edg_wll_ll_log_init(verbose ? LOG_INFO : LOG_ERR);
-#else
-   edg_wll_ll_log_init(verbose ? LOG_DEBUG : LOG_INFO);
-#endif
-   edg_wll_ll_log(LOG_INFO,"Initializing...\n");
+   if (glite_common_log_init()) {
+       fprintf(stderr,"glite_common_log_init() failed, exiting.");
+       exit(1);
+   }
+   glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Initializing...\n");
 
    /* check noParse */
    if (noParse) {
-       edg_wll_ll_log(LOG_INFO,"Parse messages for correctness... [no]\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Parse messages for correctness... [no]\n");
    } else {
-       edg_wll_ll_log(LOG_INFO,"Parse messages for correctness... [yes]\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Parse messages for correctness... [yes]\n");
    }
 
    /* check noIPC */
    if (noIPC) {
-       edg_wll_ll_log(LOG_INFO,"Send messages also to inter-logger... [no]\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Send messages also to inter-logger... [no]\n");
    } else {
-       edg_wll_ll_log(LOG_INFO,"Send messages also to inter-logger... [yes]\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Send messages also to inter-logger... [yes]\n");
    }
 
    /* check prefix correctness */
    if (strlen(prefix) > FILENAME_MAX - 34) {
-       edg_wll_ll_log(LOG_CRIT,"Too long prefix (%s) for file names, would not be able to write to log files. Exiting.\n",prefix);
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_FATAL,"Too long prefix (%s) for file names, would not be able to write to log files. Exiting.\n",prefix);
        exit(1);
    }
    /* TODO: check for write permisions */
-   edg_wll_ll_log(LOG_INFO,"Messages will be stored with the filename prefix \"%s\".\n",prefix);
+   glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Messages will be stored with the filename prefix \"%s\".\n",prefix);
 
    if (CAcert_dir)
        setenv("X509_CERT_DIR", CAcert_dir, 1);
 
-   /* initialize Globus common module */
-/* XXX: obsolete?
-   edg_wll_ll_log(LOG_INFO,"Initializing Globus common module...");
-   if (globus_module_activate(GLOBUS_COMMON_MODULE) != GLOBUS_SUCCESS) {
-       edg_wll_ll_log(LOG_NOTICE,"no.\n");
-       edg_wll_ll_log(LOG_CRIT, "Failed to initialize Globus common module. Exiting.\n");
-       exit(1);
-   } else {
-       edg_wll_ll_log(LOG_INFO,"yes.\n");
-   }
-*/
-
    /* initialize signal handling */
    if (mysignal(SIGUSR1, handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
    if (mysignal(SIGUSR2, handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
    if (mysignal(SIGPIPE, handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
-   if (mysignal(SIGHUP,  SIG_DFL) == SIG_ERR) { perror("signal"); exit(1); }
+   if (mysignal(SIGHUP,  handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
    if (mysignal(SIGINT,  handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
    if (mysignal(SIGQUIT, handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
    if (mysignal(SIGTERM, handle_signal) == SIG_ERR) { perror("signal"); exit(1); }
@@ -401,103 +452,121 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
  
    edg_wll_gss_watch_creds(cert_file,&cert_mtime);
    /* XXX DK: support noAuth */
-   ret = edg_wll_gss_acquire_cred_gsi(cert_file, key_file, &cred, &my_subject_name, 
-               &gss_stat);
+   ret = edg_wll_gss_acquire_cred_gsi(cert_file, key_file, &cred, &gss_stat);
    if (ret) {
        /* XXX DK: call edg_wll_gss_get_error() */
-       edg_wll_ll_log(LOG_CRIT,"Failed to get GSI credentials. Exiting.\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_FATAL,"Failed to get GSI credentials. Exiting.\n");
        exit(1);
    }
 
-   if (my_subject_name!=NULL) {
-       edg_wll_ll_log(LOG_INFO,"Server running with certificate: %s\n",my_subject_name);
-       free(my_subject_name);
+   if (cred->name!=NULL) {
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Server running with certificate: %s\n",cred->name);
    } else if (noAuth) {
-       edg_wll_ll_log(LOG_INFO,"Server running without certificate\n");
-#if 0
-   /* XXX DK: */    
-   } else {
-       edg_wll_ll_log(LOG_CRIT,"No server credential found. Exiting.\n");
-       exit(1);
-#endif
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Server running without certificate\n");
    }
 
    /* do listen */
-   edg_wll_ll_log(LOG_INFO,"Listening on port %d\n",port);
+   glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Listening on port %d\n",port);
    listener_fd = do_listen(port);
    if (listener_fd == -1) {
-       edg_wll_ll_log(LOG_CRIT,"Failed to listen on port %d\n",port);
-       gss_release_cred(&min_stat, &cred);
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_FATAL,"Failed to listen on port %d\n",port);
+       edg_wll_gss_release_cred(&cred, NULL);
        exit(-1);
    } else {
-       edg_wll_ll_log(LOG_DEBUG,"Listener's socket descriptor is '%d'\n",listener_fd);
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_DEBUG,"Listener's socket descriptor is '%d'\n",listener_fd);
    }
 
    client_addr_len = sizeof(client_addr);
    bzero((char *) &client_addr, client_addr_len);
 
+/* just try it before deamonizing to be able to complain aloud */
+  if (!(pidf = fopen(pidfile,"w"))) {
+        perror(pidfile);
+        exit(-1);
+  }
+  fclose(pidf);
+
+
    /* daemonize */
    if (debug) {
-       edg_wll_ll_log(LOG_INFO,"Running as daemon... [no]\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Running as daemon... [no]\n");
    } else {
-       edg_wll_ll_log(LOG_INFO,"Running as daemon... [yes]\n");
+       glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_INFO,"Running as daemon... [yes]\n");
        if (daemon(0,0) < 0) {
-               edg_wll_ll_log(LOG_CRIT,"Failed to run as daemon. Exiting.\n");
-               SYSTEM_ERROR("daemon");
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_FATAL,"Failed to run as daemon. Exiting.\n");
+               glite_common_log_SYS_ERROR("daemon");
                exit(1);
        }
    }
 
+  pidf = fopen(pidfile,"w"); assert(pidf); /* XXX */
+  fprintf(pidf,"%d\n",getpid());
+  fclose(pidf);
+
    /*
     * Main loop
     */
    while (1) {
-       edg_wll_ll_log(LOG_INFO,"Accepting incomming connections...\n");
+        int opt,my_errno;
+
+       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Accepting incomming connections...\n");
        client_fd = accept(listener_fd, (struct sockaddr *) &client_addr,
                        &client_addr_len);
+       my_errno = errno;
+       do_handle_signal();
        if (client_fd < 0) {
+               if (my_errno == EINTR) continue;
                close(listener_fd);
-               edg_wll_ll_log(LOG_CRIT,"Failed to accept incomming connections\n");
-               SYSTEM_ERROR("accept");
-               gss_release_cred(&min_stat, &cred);
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_FATAL,"Failed to accept incomming connections\n");
+               glite_common_log_SYS_ERROR("accept");
+               edg_wll_gss_release_cred(&cred, NULL);
                exit(-1);
        } else {
-               edg_wll_ll_log(LOG_DEBUG,"Incomming connection on socket '%d'\n",client_fd);
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Incomming connection on socket '%d'\n",client_fd);
+       }
+
+       opt = 0;
+       if (setsockopt(client_fd,IPPROTO_TCP,TCP_CORK,(const void *) &opt,sizeof opt)) {
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Can't reset TCP_CORK\n");
+       }
+       opt = 1;
+       if (setsockopt(client_fd,IPPROTO_TCP,TCP_NODELAY,(const void *) &opt,sizeof opt)) {
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Can't set TCP_NODELAY\n");
        }
 
        switch (edg_wll_gss_watch_creds(cert_file,&cert_mtime)) {
-       gss_cred_id_t newcred;
+       edg_wll_GssCred newcred;
        case 0: break;
        case 1:
-               ret = edg_wll_gss_acquire_cred_gsi(cert_file,key_file,&newcred,NULL,&gss_stat);
+               ret = edg_wll_gss_acquire_cred_gsi(cert_file,key_file,&newcred,&gss_stat);
                if (ret) {
-                       edg_wll_ll_log(LOG_WARNING,"Reloading credentials failed, continue with older\n");
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"Reloading credentials failed, continue with older\n");
                } else {
-                       edg_wll_ll_log(LOG_DEBUG,"Reloading credentials succeeded\n");
-                       gss_release_cred(&min_stat, &cred);
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_DEBUG,"Reloading credentials succeeded\n");
+                       edg_wll_gss_release_cred(&cred, NULL);
                        cred = newcred;
                }
                break;
        case -1:
-               edg_wll_ll_log(LOG_WARNING,"edg_wll_gss_watch_creds failed\n");
+               glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"edg_wll_gss_watch_creds failed\n");
                break;
        }
 
        /* FORK - change next line if fork() is not needed (for debugging for example) */
 #if 1
        if ((childpid = fork()) < 0) {
-               SYSTEM_ERROR("fork");
+               glite_common_log_SYS_ERROR("fork");
                if (client_fd) close(client_fd);
        }
        if (childpid == 0) {
                ret = doit(client_fd,cred,prefix,noIPC,noParse);
                if (client_fd) close(client_fd);
-               edg_wll_ll_log(LOG_DEBUG,"Exiting.\n", 
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_DEBUG,"Exiting.\n", 
                        CONNECTION_TIMEOUT);
                exit(0);
        }
        if (childpid > 0) {
-               edg_wll_ll_log(LOG_DEBUG,"Forked a new child with PID %d\n",childpid);
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_DEBUG,"Forked a new child with PID %d\n",childpid);
                if (client_fd) close(client_fd);
        }
 #else
@@ -507,8 +576,7 @@ This is LocalLogger, part of Workload Management System in EU DataGrid & EGEE.\n
 #endif
     } /* while */
 
-end:
        if (listener_fd) close(listener_fd);
-       gss_release_cred(&min_stat, &cred);
+       edg_wll_gss_release_cred(&cred, NULL);
        exit(ret);
 }
index 4e1a1e3..3e766d6 100644 (file)
@@ -1,5 +1,23 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
 
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <stdio.h>
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -9,9 +27,13 @@
 #include <string.h>
 #include <syslog.h>
 #include <fcntl.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <netdb.h>
 
+#include "glite/lbu/escape.h"
+#include "glite/lbu/log.h"
 #include "glite/lb/context-int.h"
-#include "glite/lb/escape.h"
 #include "glite/lb/events_parse.h"
 
 #include "logd_proto.h"
@@ -20,7 +42,9 @@ static const int one = 1;
 
 extern char* socket_path;
 
-int edg_wll_ll_log_level;
+int glite_common_log_priority_security;
+int glite_common_log_priority_access;
+int glite_common_log_priority_control;
 
 #define tv_sub(a,b) {\
        (a).tv_usec -= (b).tv_usec;\
@@ -34,6 +58,54 @@ int edg_wll_ll_log_level;
 /*
  *----------------------------------------------------------------------
  *
+ * handle_gss_failures - handle GSS failures on the server side
+ *
+ * Returns: errno
+ *
+ *----------------------------------------------------------------------
+ */
+static int handle_gss_failures(int code, edg_wll_GssStatus *gss_code, const char *text)
+{
+       const char      *func = "edg_wll_log_proto_server()";
+        int             ret = 0;
+
+       if(code>0) {
+                return(0);
+       }
+       switch(code) {
+               case EDG_WLL_GSS_ERROR_EOF: 
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: %s, EOF occured\n", func, text);  
+                       ret = EAGAIN;
+                       break;
+               case EDG_WLL_GSS_ERROR_TIMEOUT: 
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: %s, timeout expired\n", func, text);      
+                       ret = EAGAIN;
+                       break;
+               case EDG_WLL_GSS_ERROR_ERRNO: 
+                       glite_common_log_SYS_ERROR(func);
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: %s, system error occured\n", func, text); 
+                       ret = EAGAIN;
+                       break;
+               case EDG_WLL_GSS_ERROR_GSS:
+                       {
+                          char *gss_err;
+
+                          edg_wll_gss_get_error(gss_code, "GSS error occured", &gss_err);
+                          glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_WARN,"%s: %s, %s\n", func, text, gss_err);
+                          free(gss_err);
+                          ret = EAGAIN;
+                          break;
+                       }
+               default:
+                       glite_common_log(LOG_CATEGORY_SECURITY,LOG_PRIORITY_ERROR,"%s: %s, unknown error occured\n");
+                       break;
+       }
+       return ret;
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * send_answer_back - 
  *                     
  *----------------------------------------------------------------------
@@ -45,16 +117,15 @@ static int send_answer_back(edg_wll_GssConnection *con, int answer, struct timev
        u_int8_t ans_end[4];
        edg_wll_GssStatus       gss_stat;
 
-       edg_wll_ll_log(LOG_INFO,"Sending answer \"%d\" back to client...",answer);
         ans_end[0] = ans & 0xff; ans >>= 8;
         ans_end[1] = ans & 0xff; ans >>= 8;
         ans_end[2] = ans & 0xff; ans >>= 8;
         ans_end[3] = ans;
        if ((err = edg_wll_gss_write_full(con,ans_end,4,timeout,&count, &gss_stat)) < 0 ) {
-               edg_wll_ll_log(LOG_INFO,"error.\n");
-               return edg_wll_log_proto_server_failure(err,&gss_stat,"Error sending answer");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Error sending answer \"%d\" back to client.\n",answer);
+               return handle_gss_failures(err,&gss_stat,"Error sending answer");
        } else {
-               edg_wll_ll_log(LOG_INFO,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Answer \"%d\" succesfully sent back to client.\n",answer);
                return 0;
        }
 }
@@ -82,8 +153,8 @@ int init_confirmation()
 
        /* create socket */
        if((confirm_sock=socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
-               SYSTEM_ERROR("socket");
-               edg_wll_ll_log(LOG_ERR,"init_confirmation(): error creating socket\n");
+               glite_common_log_SYS_ERROR("socket");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"init_confirmation(): error creating socket\n");
                return(-1);
        }
 
@@ -94,8 +165,8 @@ int init_confirmation()
 
        /* bind the socket */
        if(bind(confirm_sock, (struct sockaddr *)&saddr, sizeof(saddr.sun_path)) < 0) {
-               SYSTEM_ERROR("bind");
-               edg_wll_ll_log(LOG_ERR,"init_confirmation(): error binding socket\n");
+               glite_common_log_SYS_ERROR("bind");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"init_confirmation(): error binding socket\n");
                close(confirm_sock);
                unlink(confirm_sock_name);
                return(-1);
@@ -103,8 +174,8 @@ int init_confirmation()
 
        /* and listen */
        if(listen(confirm_sock, 5) < 0) {
-               SYSTEM_ERROR("listen");
-               edg_wll_ll_log(LOG_ERR,"init_confirmation(): error listening on socket\n");
+               glite_common_log_SYS_ERROR("listen");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"init_confirmation(): error listening on socket\n");
                close(confirm_sock);
                unlink(confirm_sock_name);
                return(-1);
@@ -133,8 +204,8 @@ int wait_for_confirmation(struct timeval *timeout, int *code)
 
        /* wait for confirmation at most timeout seconds */
        if ((tmp=select(confirm_sock+1, &fds, NULL, NULL, timeout?&to:NULL)) < 0) {
-               SYSTEM_ERROR("select");
-               edg_wll_ll_log(LOG_ERR,"wait_for_confirmation(): error selecting socket\n");
+               glite_common_log_SYS_ERROR("select");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"wait_for_confirmation(): error selecting socket\n");
                ret = -1;
        } else {
                if (tmp == 0)
@@ -143,13 +214,13 @@ int wait_for_confirmation(struct timeval *timeout, int *code)
                        int nsd = accept(confirm_sock, NULL, NULL);
                        ret = 1;
                        if(nsd < 0) {
-                               SYSTEM_ERROR("accept");
-                               edg_wll_ll_log(LOG_ERR,"wait_for_confirmation(): error accepting a connection on a socket\n");
+                               glite_common_log_SYS_ERROR("accept");
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"wait_for_confirmation(): error accepting a connection on a socket\n");
                                ret = -1;
                        } else {
                                if(recv(nsd, code, sizeof(*code), MSG_NOSIGNAL) < 0) {
-                                       SYSTEM_ERROR("recv");
-                                       edg_wll_ll_log(LOG_ERR,"wait_for_confirmation(): error receiving a message from a socket\n");
+                                       glite_common_log_SYS_ERROR("recv");
+                                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"wait_for_confirmation(): error receiving a message from a socket\n");
                                        ret = -1;
                                }
                                close(nsd);
@@ -187,32 +258,52 @@ int do_listen(int port)
 {
        int                ret;
        int                sock;
-       struct sockaddr_in my_addr;
+       struct addrinfo *ai;
+       struct addrinfo hints;
+       char            *portstr = NULL;
+
+       asprintf(&portstr, "%d", port);
+       if (portstr == NULL) {
+               glite_common_log(LOG_CATEGORY_CONTROL,LOG_PRIORITY_FATAL,"do_listen(): ENOMEM converting port number\n");
+               return -1;
+       }
+
+       memset (&hints, '\0', sizeof (hints));
+       hints.ai_flags = AI_NUMERICSERV | AI_PASSIVE | AI_ADDRCONFIG;
+       hints.ai_socktype = SOCK_STREAM;
 
-       memset(&my_addr, 0, sizeof(my_addr));
-       my_addr.sin_family = AF_INET;
-       my_addr.sin_addr.s_addr = INADDR_ANY;
-       my_addr.sin_port = htons(port);
+       ret = getaddrinfo (NULL, portstr, &hints, &ai);
+       if (ret != 0) {
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "getaddrinfo: %s", gai_strerror (ret));
+               return -1;
+       }
+       if (ai == NULL) {
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, "getaddrinfo: no return");
+               return -1;
+       }
 
-       sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+       sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
        if (sock == -1) { 
-               SYSTEM_ERROR("socket"); 
-               edg_wll_ll_log(LOG_ERR,"do_listen(): error creating socket\n");
+               glite_common_log_SYS_ERROR("socket"); 
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"do_listen(): error creating socket\n");
+               freeaddrinfo(ai);
                return -1; 
        }
 
        setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
-       ret = bind(sock, (struct sockaddr *)&my_addr, sizeof(my_addr));
+       ret = bind(sock, ai->ai_addr, ai->ai_addrlen);
        if (ret == -1) { 
-               SYSTEM_ERROR("bind"); 
-               edg_wll_ll_log(LOG_ERR,"do_listen(): error binding socket\n");
+               glite_common_log_SYS_ERROR("bind"); 
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"do_listen(): error binding socket\n");
+               freeaddrinfo(ai);
                return -1; 
        }
+       freeaddrinfo(ai);
 
        ret = listen(sock, 5);
        if (ret == -1) { 
-               SYSTEM_ERROR("listen"); 
-               edg_wll_ll_log(LOG_ERR,"do_listen(): error listening on socket\n");
+               glite_common_log_SYS_ERROR("listen"); 
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"do_listen(): error listening on socket\n");
                close(sock); 
                return -1; 
        }
@@ -245,10 +336,8 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
        int     i,answer,answer_sent;
        int     msg_sock;
        char    *msg,*msg_begin;
-       FILE    *outfile;
-       int     filedesc,filelock_status,flags;
+       int     filedesc,filelock_status;
        long    filepos;
-       struct flock filelock;
        int     priority;
        long    lllid;
        int     unique;
@@ -264,12 +353,12 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
 
        /* init */
        if (edg_wll_InitContext(&context) != 0) {
-               edg_wll_ll_log(LOG_ERR,"edg_wll_InitContex(): error.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"edg_wll_InitContex(): error.\n");
                answer = ENOMEM; 
                goto edg_wll_log_proto_server_end; 
        }
        if (edg_wll_ResetError(context) != 0) { 
-               edg_wll_ll_log(LOG_ERR,"edg_wll_ResetError(): error.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"edg_wll_ResetError(): error.\n");
                answer = ENOMEM; 
                goto edg_wll_log_proto_server_end;
        }
@@ -281,9 +370,9 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
                snprintf(confirm_sock_name, sizeof(confirm_sock_name), "/tmp/dglogd_sock_%ld", lllid);
                if ((filedesc = open(confirm_sock_name,O_CREAT)) == -1) {
                        if (errno == EEXIST) {
-                               edg_wll_ll_log(LOG_WARNING,"Warning: LLLID %ld already in use.\n",lllid);
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Warning: LLLID %ld already in use.\n",lllid);
                        } else {
-                               SYSTEM_ERROR("open");
+                               glite_common_log_SYS_ERROR("open");
                        }
                } else {
                        unique = 1;
@@ -292,94 +381,88 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
                }
        }
        if (!unique) {
-               edg_wll_ll_log(LOG_ERR,"Cannot determine the unique long local-logger id (LLLID)!\n",lllid);
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_ERROR,"Cannot determine the unique long local-logger id (LLLID)!\n",lllid);
                return EAGAIN;
        }
-       edg_wll_ll_log(LOG_INFO,"Long local-logger id (LLLID): %ld\n",lllid);
+       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Long local-logger id (LLLID): %ld ... [ok]\n",lllid);
 
        /* receive socket header */
-       edg_wll_ll_log(LOG_INFO,"Reading socket header...");
        memset(header, 0, EDG_WLL_LOG_SOCKET_HEADER_LENGTH+1);
        if ((err = edg_wll_gss_read_full(con, header, EDG_WLL_LOG_SOCKET_HEADER_LENGTH, timeout, &count, &gss_stat)) < 0) {
                if (err == EDG_WLL_GSS_ERROR_EOF) {
-                       edg_wll_ll_log(LOG_INFO,"no data available.\n");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Reading socket header - no data available.\n");
                        answer = err;
                        answer_sent = 1; /* i.e. do not try to send answer back */
                } else {
-                       edg_wll_ll_log(LOG_INFO,"error.\n");
-                       answer = edg_wll_log_proto_server_failure(err,&gss_stat,"Error receiving header");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Error reading socket header.\n");
+                       answer = handle_gss_failures(err,&gss_stat,"Error reading socket header");
                }
                goto edg_wll_log_proto_server_end;
        } else {
-               edg_wll_ll_log(LOG_INFO,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Reading socket header... [ok]\n");
        }
 
-       edg_wll_ll_log(LOG_DEBUG,"Checking socket header...");
+       /* Check socket header */
        header[EDG_WLL_LOG_SOCKET_HEADER_LENGTH] = '\0';
        if (strncmp(header,EDG_WLL_LOG_SOCKET_HEADER,EDG_WLL_LOG_SOCKET_HEADER_LENGTH)) {
                /* not the proper socket header text */
-               edg_wll_ll_log(LOG_DEBUG,"error.\n");
-               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): invalid socket header\n");
-               edg_wll_ll_log(LOG_DEBUG,"edg_wll_log_proto_server(): read header '%s' instead of '%s'\n",
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): invalid socket header\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"edg_wll_log_proto_server(): read header '%s' instead of '%s'\n",
                                header,EDG_WLL_LOG_SOCKET_HEADER);
                answer = EINVAL;
                goto edg_wll_log_proto_server_end;
        } else {
-               edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Read socket header: \"%s\" [ok]\n",header);
        }
 
 /* XXX: obsolete
-       edg_wll_ll_log(LOG_DEBUG,"Reading message priority...");
        count = 0;
        if ((err = edg_wll_gss_read_full(con, &priority, sizeof(priority), timeout, &count, &gss_stat)) < 0) {
-               edg_wll_ll_log(LOG_DEBUG,"error.\n");
-               answer = edg_wll_log_proto_server_failure(err,&gss_stat,"Error receiving message priority");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Error reading message priority.\n");
+               answer = handle_gss_failures(err,&gss_stat,"Error receiving message priority");
                 goto edg_wll_log_proto_server_end;
         } else {
-               edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Read message priority: %d [ok]\n",priority);
        }
 */
 
-        edg_wll_ll_log(LOG_DEBUG,"Reading message size...");
+        /* read message size */
        count = 0;
        if ((err = edg_wll_gss_read_full(con, size_end, 4, timeout, &count,&gss_stat)) < 0) {
-               edg_wll_ll_log(LOG_DEBUG,"error.\n");
-               answer = edg_wll_log_proto_server_failure(err,&gss_stat,"Error receiving message size");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Error reading message size.\n");
+               answer = handle_gss_failures(err,&gss_stat,"Error reading message size");
                 goto edg_wll_log_proto_server_end;
        } else {
-               edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Reading message size... [ok]\n");
        }
        size = size_end[3]; size <<=8; 
        size |= size_end[2]; size <<=8; 
        size |= size_end[1]; size <<=8; 
        size |= size_end[0];
-        edg_wll_ll_log(LOG_DEBUG,"Checking message size...");
        if (size <= 0) {
-               edg_wll_ll_log(LOG_DEBUG,"error.\n");
                /* probably wrong size in the header or nothing to read */
-               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): invalid size read from socket header\n");
-               edg_wll_ll_log(LOG_DEBUG,"Read size '%d'.\n",size);
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): invalid size read from socket header\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Read message size '%d' [error].\n",size);
                answer = EINVAL;
                goto edg_wll_log_proto_server_end;
        } else {
-               edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
-               edg_wll_ll_log(LOG_DEBUG,"- Size read from header: %d bytes.\n",size);
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Read message size: %d bytes [ok].\n",size);
        }
 
        /* format the DG.LLLID string */
        if (asprintf(&dglllid,"DG.LLLID=%ld ",lllid) == -1) {
-               SYSTEM_ERROR("asprintf");
-               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): nomem for DG.LLLID\n");
+               glite_common_log_SYS_ERROR("asprintf");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): nomem for DG.LLLID\n");
                answer = ENOMEM;
                goto edg_wll_log_proto_server_end;
        }
        dglllid_size = strlen(dglllid);
 
        /* format the DG.USER string */
-       name_esc = edg_wll_LogEscape(name);
+       name_esc = glite_lbu_EscapeULM(name);
        if (asprintf(&dguser,"DG.USER=\"%s\" ",name_esc) == -1) {
-               SYSTEM_ERROR("asprintf");
-               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): nomem for DG.USER\n");
+               glite_common_log_SYS_ERROR("asprintf");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): nomem for DG.USER\n");
                answer = ENOMEM;
                goto edg_wll_log_proto_server_end;
        }
@@ -388,8 +471,8 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
        /* allocate enough memory for all data */
        msg_size = dglllid_size + dguser_size + size + 1;
        if ((msg = malloc(msg_size)) == NULL) {
-               SYSTEM_ERROR("malloc");
-               edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): out of memory for allocating message\n");
+               glite_common_log_SYS_ERROR("malloc");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): out of memory for allocating message\n");
                answer = ENOMEM;
                goto edg_wll_log_proto_server_end;
        }
@@ -398,52 +481,48 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
        strncpy(msg_begin,dguser,dguser_size);
 
        /* receive message */
-       edg_wll_ll_log(LOG_INFO,"Reading message from socket...");
        buf = msg_begin + dguser_size;
        count = 0;
        if ((err = edg_wll_gss_read_full(con, buf, size, timeout, &count, &gss_stat)) < 0) {
-               edg_wll_ll_log(LOG_INFO,"error.\n");
-               answer = edg_wll_log_proto_server_failure(err,&gss_stat,"Error receiving message");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Error reading message from socket.\n");
+               answer = handle_gss_failures(err,&gss_stat,"Error reading message from socket.");
                goto edg_wll_log_proto_server_end;
        } else {
-               edg_wll_ll_log(LOG_INFO,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Reading message... [ok]\n");
+               // glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_TRACE,"Read message: \"%s\"\n",msg);
        }       
 
        if (buf[count] != '\0') buf[count] = '\0';
 
        /* parse message and get jobId and priority from it */
        if (!noparse && strstr(msg, "DG.TYPE=\"command\"") == NULL) {
-               edg_wll_ll_log(LOG_INFO,"Parsing message for correctness...");
                if (edg_wll_ParseEvent(context,msg_begin,&event) != 0) { 
-                       edg_wll_ll_log(LOG_INFO,"error.\n");
-                       edg_wll_ll_log(LOG_ERR,"edg_wll_log_proto_server(): edg_wll_ParseEvent error\n");
-                       edg_wll_ll_log(LOG_ERR,"edg_wll_ParseEvent(): %s\n",context->errDesc);
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_proto_server(): edg_wll_ParseEvent error\n");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_ParseEvent(): %s\n",context->errDesc);
                        answer = edg_wll_Error(context,NULL,NULL);
                        goto edg_wll_log_proto_server_end;
                } else {
-                       edg_wll_ll_log(LOG_INFO,"o.k.\n");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Parsing message for correctness...[ok]\n");
                }
-               edg_wll_ll_log(LOG_DEBUG,"Getting jobId from message...");
                jobId = edg_wlc_JobIdGetUnique(event->any.jobId);
                priority = event->any.priority;
                edg_wll_FreeEvent(event);
                event->any.priority = priority;
-               edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Getting jobId from message...[ok]\n");
        } else {
                if ((event = edg_wll_InitEvent(EDG_WLL_EVENT_UNDEF)) == NULL) {
-                       edg_wll_ll_log(LOG_ERR, "edg_wll_InitEvent(): out of memory\n");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN, "edg_wll_InitEvent(): out of memory\n");
                        answer = ENOMEM;
                        goto edg_wll_log_proto_server_end;
                }
-               edg_wll_ll_log(LOG_DEBUG,"Getting jobId from message...");
                jobId = edg_wll_GetJobId(msg);
                if (!jobId || edg_wlc_JobIdParse(jobId,&j)) {
-                       edg_wll_ll_log(LOG_DEBUG,"error.\n");
-                       edg_wll_ll_log(LOG_ERR,"ParseJobId(%s)\n",jobId?jobId:"NULL");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Error getting jobId from message.\n");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wlc_JobIdParse(%s)\n",jobId?jobId:"NULL");
                        answer = EINVAL;
                        goto edg_wll_log_proto_server_end;
                } else {
-                       edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Getting jobId from message...[ok]\n");
                }
                free(jobId);
                jobId = edg_wlc_JobIdGetUnique(j);
@@ -455,45 +534,43 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
                else event->any.priority = 0;
        }
 
-
        /* if not command, save message to file */
        if(strstr(msg, "DG.TYPE=\"command\"") == NULL) {
                /* compose the name of the log file */
-//             edg_wll_ll_log(LOG_DEBUG,"Composing filename from prefix \"%s\" and unique jobId \"%s\"...",prefix,jobId);
                count = strlen(prefix);
                strncpy(outfilename,prefix,count); count_total=count;
                strncpy(outfilename+count_total,".",1); count_total+=1; count=strlen(jobId);
                strncpy(outfilename+count_total,jobId,count); count_total+=count;
                outfilename[count_total]='\0';
-//             edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+//             glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Composing filename from prefix \"%s\" and unique jobId \"%s\"...[ok]",prefix,jobId);
 
                /* fopen and properly handle the filelock */
 #ifdef LOGD_NOFILE
-               edg_wll_ll_log(LOG_NOTICE,"NOT writing message to \"%s\".\n",outfilename);
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"NOT writing message to \"%s\".\n",outfilename);
                filepos = 0;
 #else
-               edg_wll_ll_log(LOG_INFO,"Writing message to \"%s\"...",outfilename);
                if ( edg_wll_log_event_write(context, outfilename, msg, FCNTL_ATTEMPTS, FCNTL_TIMEOUT, &filepos) ) {
                        char *errd;
-                       SYSTEM_ERROR("edg_wll_log_event_write");
+                       // FIXME: there is probably not a correct errno
+                       glite_common_log_SYS_ERROR("edg_wll_log_event_write");
                        answer = edg_wll_Error(context, NULL, &errd);
-                       edg_wll_ll_log(LOG_ERR,"edg_wll_log_event_write error: %s\n",errd);
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_event_write error: %s\n",errd);
                        free(errd);
                        goto edg_wll_log_proto_server_end;
-               } else edg_wll_ll_log(LOG_INFO,"o.k.\n");
+               } else glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Writing message to \"%s\"... [ok]",outfilename);
 #endif
        } else {
                filepos = 0;
        }
 
 #ifdef LB_PERF
-       edg_wll_ll_log(LOG_INFO,"Calling perftest\n");
+       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Calling perftest\n");
        glite_wll_perftest_consumeEventString(msg);
-       edg_wll_ll_log(LOG_INFO,"o.k.\n");
+       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Calling perftest... [done]\n");
 #endif
 
        /* if not priority send now the answer back to client */
-       if (!event->any.priority) {
+       if (!(event->any.priority & (EDG_WLL_LOGFLAG_SYNC|EDG_WLL_LOGFLAG_SYNC_COMPAT))) {
                if (!send_answer_back(con,answer,timeout)) { 
                        answer_sent = 1;
                }
@@ -501,48 +578,45 @@ int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout
 
        /* send message via IPC (UNIX socket) */
        if (!noipc) {
-               if (event->any.priority) {
-                       edg_wll_ll_log(LOG_DEBUG,"Initializing 2nd UNIX socket (%s) for priority messages confirmation...",confirm_sock_name);
+               if (event->any.priority & (EDG_WLL_LOGFLAG_SYNC|EDG_WLL_LOGFLAG_SYNC_COMPAT)) {
                        if(init_confirmation() < 0) { 
-                               edg_wll_ll_log(LOG_DEBUG,"error.\n");
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Error initializing 2nd UNIX socket (%s) for priority messages confirmation.\n",confirm_sock_name); 
                                answer = errno; 
                                goto edg_wll_log_proto_server_end; 
                        } else {
-                               edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Initializing 2nd UNIX socket (%s) for priority messages confirmation...[ok]\n",confirm_sock_name);
                        }
                }         
 
-               edg_wll_ll_log(LOG_DEBUG,
-                       "Sending via IPC (UNIX socket \"%s\")\n\t"
-                       "the message position %ld (%d bytes)",
-                       socket_path, filepos, sizeof(filepos));
                if ( edg_wll_log_event_send(context, socket_path, filepos, msg, msg_size, CONNECT_ATTEMPTS, timeout) ) {
                        char *errd;
-                       SYSTEM_ERROR("edg_wll_log_event_send");
+                       // XXX: probably not a SYSTEM ERROR
+                       // glite_common_log_SYS_ERROR("edg_wll_log_event_send");
                        answer = edg_wll_Error(context, NULL, &errd);
-                       edg_wll_ll_log(LOG_ERR,"edg_wll_log_event_send error: %s\n",errd);
+                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"edg_wll_log_event_send error: %s\n",errd);
                        free(errd);
                        goto edg_wll_log_proto_server_end_1;
-               } else edg_wll_ll_log(LOG_DEBUG,"o.k.\n");
+               } else glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,
+                       "Sending via IPC (UNIX socket \"%s\")\n\t"
+                       "the message position %ld (%d bytes)... [ok]",
+                       socket_path, filepos, sizeof(filepos));
 
-               if (event->any.priority) {
-                       edg_wll_ll_log(LOG_INFO,"Waiting for confirmation...");
+               if (event->any.priority & (EDG_WLL_LOGFLAG_SYNC|EDG_WLL_LOGFLAG_SYNC_COMPAT)) {
                        if ((count = wait_for_confirmation(timeout, &answer)) < 0) {
-                               edg_wll_ll_log(LOG_INFO,"error.\n");
-                               edg_wll_ll_log(LOG_ERR,"wait_for_confirmation(): error.\n"); 
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_WARN,"Error waiting for confirmation.\n");
                                answer = errno;
                        } else {
-                               edg_wll_ll_log(LOG_INFO,"o.k.\n");
+                               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Waiting for confirmation... [ok].\n");
                                if (count == 0) {
-                                       edg_wll_ll_log(LOG_DEBUG,"Waking up, timeout expired.\n");
+                                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Waking up, timeout expired.\n");
                                        answer = EAGAIN;
                                } else {
-                                       edg_wll_ll_log(LOG_DEBUG,"Confirmation received, waking up.\n");
+                                       glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"Confirmation received, waking up.\n");
                                }
                        }
                }
        } else {
-               edg_wll_ll_log(LOG_DEBUG,"NOT sending via IPC.\n");
+               glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_DEBUG,"NOT sending via IPC.\n");
        }
 
 edg_wll_log_proto_server_end:
@@ -559,7 +633,7 @@ edg_wll_log_proto_server_end:
        if (msg) free(msg);
        if (event) free(event);
 
-//     edg_wll_ll_log(LOG_INFO,"Done.\n");
+//     glite_common_log(LOG_CATEGORY_ACCESS,LOG_PRIORITY_INFO,"Done.\n");
 
        return answer;
 
@@ -571,88 +645,3 @@ edg_wll_log_proto_server_end_1:
        goto edg_wll_log_proto_server_end;
 }
 
-/*
- *----------------------------------------------------------------------
- *
- * edg_wll_log_proto_server_failure - handle protocol failures on the server side
- *
- * Returns: errno
- *
- *----------------------------------------------------------------------
- */
-int edg_wll_log_proto_server_failure(int code, edg_wll_GssStatus *gss_code, const char *text)
-{
-       const char      *func = "edg_wll_log_proto_server()";
-        int             ret = 0;
-
-       if(code>0) {
-                return(0);
-       }
-       switch(code) {
-               case EDG_WLL_GSS_ERROR_EOF: 
-                       edg_wll_ll_log(LOG_ERR,"%s: %s, EOF occured\n", func, text);    
-                       ret = EAGAIN;
-                       break;
-               case EDG_WLL_GSS_ERROR_TIMEOUT: 
-                       edg_wll_ll_log(LOG_ERR,"%s: %s, timeout expired\n", func, text);        
-                       ret = EAGAIN;
-                       break;
-               case EDG_WLL_GSS_ERROR_ERRNO: 
-                       SYSTEM_ERROR(func);
-                       edg_wll_ll_log(LOG_ERR,"%s: %s, system error occured\n", func, text);   
-                       ret = EAGAIN;
-                       break;
-               case EDG_WLL_GSS_ERROR_GSS:
-                       {
-                          char *gss_err;
-
-                          edg_wll_gss_get_error(gss_code, "GSS error occured", &gss_err);
-                          edg_wll_ll_log(LOG_ERR,"%s: %s, %s\n", func, text, gss_err);
-                          free(gss_err);
-                          ret = EAGAIN;
-                          break;
-                       }
-               default:
-                       edg_wll_ll_log(LOG_ERR,"%s: %s, unknown error occured\n");
-                       break;
-       }
-       return ret;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * edg_wll_ll_log_init - initialize the logging level
- *
- *----------------------------------------------------------------------
- */
-void edg_wll_ll_log_init(int level) {
-       edg_wll_ll_log_level = level;
-}
-
-/*
- *----------------------------------------------------------------------
- *
- * edg_wll_ll_log - print to stderr according to logging level
- *   serious messages are also written to syslog
- *
- *----------------------------------------------------------------------
- */
-void edg_wll_ll_log(int level, const 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 <= edg_wll_ll_log_level) 
-               fprintf(stderr, "[%d] %s", (int) getpid(), err_text);
-       if(level <= LOG_ERR) {
-               openlog(NULL, LOG_PID | LOG_CONS, LOG_DAEMON);
-               syslog(level, "%s", err_text);
-               closelog();
-       }
-
-       if (err_text) free(err_text);
-}
index 2754e4a..993eec3 100644 (file)
@@ -2,6 +2,23 @@
 #define __EDG_WORKLOAD_LOGGING_LOCALLOGGER_LOGD_PROTO_H__
 
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 /**
  * \file edg/workload/logging/locallogger/logd_proto.h
 extern "C" {
 #endif
 
-
 #include <syslog.h>
 
 #include "glite/lb/log_proto.h"
 #include "glite/security/glite_gss.h"
 
 int edg_wll_log_proto_server(edg_wll_GssConnection *con, struct timeval *timeout, char *name, char *prefix, int noipc, int noparse);
-int edg_wll_log_proto_server_failure(int code, edg_wll_GssStatus *gss_code, const char *text);
-
-#define SYSTEM_ERROR(my_err) { \
-       if (errno !=0 ) \
-               edg_wll_ll_log(LOG_ERR,"%s: %s\n",my_err,strerror(errno)); \
-       else \
-               edg_wll_ll_log(LOG_ERR,"%s\n",my_err); }
-
-/* locallogger daemon error handling */
-extern int edg_wll_ll_log_level;
-void edg_wll_ll_log_init(int level);
-void edg_wll_ll_log(int level, const char *fmt, ...);
-
 
 /* fcntl defaults */
 #define FCNTL_ATTEMPTS         5
index 6121555..45d16df 100644 (file)
@@ -1,4 +1,20 @@
 #!/bin/bash
+#
+# Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+# See http://www.eu-egee.org/partners for details on the copyright holders.
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 
 numjobs=10
 
@@ -11,11 +27,14 @@ fi
 
 . $STAGEDIR/sbin/perftest_common.sh
 
+DBNAME=${DBNAME:-lbserver20}
+export LBDB=lbserver/@localhost:$DBNAME
+
 DEBUG=${DEBUG:-0}
 # CONSUMER_ARGS=
 # PERFTEST_COMPONENT=
 # COMPONENT_ARGS=
-#LOGJOBS_ARGS="
+LOGJOBS_ARGS="-s /tmp/interlogger.perftest
 
 check_test_files || exit 1
 
@@ -59,7 +78,7 @@ echo ""
 fi
 
 PERFTEST_CONSUMER=$STAGEDIR/bin/glite-lb-interlogd-perf-empty
-CONSUMER_ARGS="-d $COMM_ARGS"
+CONSUMER_ARGS="-i /tmp/perftest_il.pid -d $COMM_ARGS"
 }
 
 group_a_test_a () 
@@ -75,7 +94,7 @@ group_a_test_b () {
     echo -n "b)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm -f \{\} \;
 }
 
 
@@ -141,38 +160,38 @@ LOGJOBS_ARGS=" $COMM_ARGS"
 
 group_b_test_a ()
 {
-    CONSUMER_ARGS="-d --nosend --noparse $COMM_ARGS"
+    CONSUMER_ARGS="-i /tmp/perftest_il.pid -d --nosend --noparse $COMM_ARGS"
     echo -n "a)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_b_test_b () 
 {
-    CONSUMER_ARGS="-d --nosend --nosync $COMM_ARGS"
+    CONSUMER_ARGS="-i /tmp/perftest_il.pid -d --nosend --nosync $COMM_ARGS"
     echo -n "b)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_b_test_c () 
 {
-    CONSUMER_ARGS="-d --nosend --norecover $COMM_ARGS"
+    CONSUMER_ARGS="-i /tmp/perftest_il.pid -d --nosend --norecover $COMM_ARGS"
     echo -n "c)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_b_test_x ()
 {
-    CONSUMER_ARGS="-d --nosend --nosync --norecover $COMM_ARGS"
+    CONSUMER_ARGS="-i /tmp/perftest_il.pid -d --nosend --nosync --norecover $COMM_ARGS"
     echo -n "x)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_b_test_d ()
@@ -182,11 +201,11 @@ group_b_test_d ()
 
 group_b_test_e ()
 {
-    CONSUMER_ARGS="-d --nosend $COMM_ARGS"
+    CONSUMER_ARGS="-i /tmp/perftest_il.pid -d --nosend $COMM_ARGS"
     echo -n "e)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 # echo "-------------------------------"
@@ -254,63 +273,63 @@ echo ""
 fi
 
 PERFTEST_CONSUMER=$STAGEDIR/bin/glite-lb-bkserverd
-CONSUMER_ARGS="-d --perf-sink=1"
+CONSUMER_ARGS="--silent -S /tmp -D /tmp -t 1 -d --perf-sink=1 -p 10500 -w 10503"
 PERFTEST_COMPONENT=$STAGEDIR/bin/glite-lb-interlogd-perf
-LOGJOBS_ARGS=" $COMM_ARGS"
+LOGJOBS_ARGS=" -m localhost:10500 $COMM_ARGS"
 }
 
 group_c_test_a ()
 {
-    COMPONENT_ARGS="-d  --noparse $COMM_ARGS"
+    COMPONENT_ARGS="-i /tmp/perftest_il.pid -d  --noparse $COMM_ARGS"
     echo -n "a)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_c_test_b ()
 {
-    COMPONENT_ARGS="-d  --nosync $COMM_ARGS"
+    COMPONENT_ARGS="-i /tmp/perftest_il.pid -d  --nosync $COMM_ARGS"
     echo -n "b)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_c_test_c ()
 {
-    COMPONENT_ARGS="-d  --norecover $COMM_ARGS"
+    COMPONENT_ARGS="-i /tmp/perftest_il.pid -d  --norecover $COMM_ARGS"
     echo -n "c)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_c_test_x () 
 {
-    COMPONENT_ARGS="-d  --nosync --norecover $COMM_ARGS"
+    COMPONENT_ARGS="-i /tmp/perftest_il.pid -d  --nosync --norecover $COMM_ARGS"
     echo -n "x)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_c_test_d ()
 {
-    COMPONENT_ARGS="-d  --lazy=10 --nosync --norecover $COMM_ARGS"
+    COMPONENT_ARGS="-i /tmp/perftest_il.pid -d  --lazy=10 --nosync --norecover $COMM_ARGS"
     echo -n "d)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
 group_c_test_e ()
 {
-    COMPONENT_ARGS="-d $COMM_ARGS"
+    COMPONENT_ARGS="-i /tmp/perftest_il.pid -d $COMM_ARGS"
     echo -n "e)"
     run_test il $numjobs
     print_result
-    rm -f /tmp/perftest.log.*
+    find /tmp -maxdepth 1 -name perftest.log.\* -exec rm \{\} \;
 }
 
    
@@ -344,7 +363,7 @@ do
            echo -n "Your choice: "
            read -e TEST_VARIANT
        done
-       echo -e "\tavg_job \t big_job \t avg_dag \t big_dag"
+       print_result_header
     fi
 
     if [[ "x$TEST_VARIANT" = "x*" ]]
@@ -357,6 +376,7 @@ do
 
     for variant in $TEST_VARIANT
     do
+       export PERFTEST_NAME="il_${group}${variant}"
        group_${group}_test_${variant}
     done
 done
index d6fc67d..d8eec47 100644 (file)
@@ -1,4 +1,20 @@
 #!/bin/bash
+#
+# Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+# See http://www.eu-egee.org/partners for details on the copyright holders.
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+# 
+#     http://www.apache.org/licenses/LICENSE-2.0
+# 
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 
 numjobs=1
 
@@ -114,7 +130,7 @@ then
        echo -n "Your choice: "
        read -e TEST_VARIANT
     done
-    echo -e "\tavg_job \t big_job \t avg_dag \t big_dag"
+    print_result_header
 fi
 
 if [[ "x$TEST_VARIANT" = "x*" ]]
diff --git a/org.glite.lb.logger/src/plugin_mgr.c b/org.glite.lb.logger/src/plugin_mgr.c
new file mode 100644 (file)
index 0000000..0611ba8
--- /dev/null
@@ -0,0 +1,88 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include "interlogd.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+
+struct plugin_list {
+       struct il_output_plugin plugin_def;
+       struct plugin_list *next;
+};
+
+static struct plugin_list *plugins = NULL;
+
+#define DL_RESOLVESYM(var, handle, name, type) \
+       dlerror(); \
+       var = (type) dlsym(handle, name); \
+       if(var == NULL) { \
+               snprintf(err, sizeof(err), "plugin_init: error resolving %s: %s", name, dlerror()); \
+               set_error(IL_DL, ENOENT, err); \
+               return -1; \
+       }
+
+int plugin_mgr_init(const char *plugin_name, char *cfg)
+{
+       char err[256];
+       void *dl_handle;
+       struct plugin_list *plugin;
+
+       dlerror();
+       dl_handle = dlopen(plugin_name, RTLD_LAZY);
+       if(dl_handle == NULL) {
+               snprintf(err, sizeof(err), "plugin_init: error opening dynamic library: %s", dlerror());
+               set_error(IL_SYS, ENOENT, err);
+               return -1;
+       }
+       dlerror();
+
+       plugin = malloc(sizeof(*plugin));
+       if(plugin == NULL) {
+               set_error(IL_NOMEM, ENOMEM, "plugin_init: error allocating plugin description");
+               return -1;
+       }
+
+       plugin->next = plugins;
+       plugins = plugin;
+       DL_RESOLVESYM(plugin->plugin_def.plugin_init, dl_handle, "plugin_init", int(*)(char *));
+       DL_RESOLVESYM(plugin->plugin_def.plugin_supports_scheme, dl_handle,  "plugin_supports_scheme", int(*)(const char *));
+       DL_RESOLVESYM(plugin->plugin_def.event_queue_connect, dl_handle, "event_queue_connect", int (*)(struct event_queue*));
+       DL_RESOLVESYM(plugin->plugin_def.event_queue_send, dl_handle, "event_queue_send", int (*)(struct event_queue *));
+       DL_RESOLVESYM(plugin->plugin_def.event_queue_close, dl_handle, "event_queue_close", int (*)(struct event_queue *));
+
+       return (*plugin->plugin_def.plugin_init)(cfg);
+}
+
+
+struct il_output_plugin *
+plugin_get(const char *scheme)
+{
+       struct plugin_list *outp;
+
+       for(outp = plugins; outp != NULL; outp = outp->next) {
+               if((outp->plugin_def.plugin_supports_scheme)(scheme)) {
+                       return &outp->plugin_def;
+               }
+       }
+
+       return NULL;
+}
index b76c397..ac05324 100644 (file)
@@ -1,10 +1,29 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <string.h>
 #include <errno.h>
 #include <assert.h>
+#include <stdio.h>
 
-#include "glite/lb/consumer.h"
+#include "glite/jobid/cjobid.h"
+#include "glite/lb/context.h"
 
 #include "interlogd.h"
 
@@ -17,7 +36,9 @@ struct queue_list {
 #endif
 };
 
+#if !defined(IL_NOTIFICATIONS)
 static struct event_queue *log_queue;
+#endif
 static struct queue_list  *queues;
 
 
@@ -88,7 +109,7 @@ queue_list_add(struct queue_list **ql, const char *dest, struct event_queue *eq)
     return(-1);
   }
   el->queue = eq;
-  el->next = queues;
+  el->next = *ql;
   *ql = el;
   return 0;
 }
@@ -122,6 +143,8 @@ queue_list_get(char *job_id_s)
   char *dest;
   struct queue_list *q;
   struct event_queue *eq;
+  struct il_output_plugin *outp;
+
 #if !defined(IL_NOTIFICATIONS)
   IL_EVENT_ID_T job_id;
 
@@ -135,8 +158,10 @@ queue_list_get(char *job_id_s)
 
   dest = jobid2dest(job_id);
   edg_wlc_JobIdFree(job_id);
+  outp = NULL;
 #else
   dest = job_id_s;
+  outp = plugin_get(dest);
 #endif
 
   if(dest == NULL) 
@@ -148,7 +173,7 @@ queue_list_get(char *job_id_s)
 #endif
     return(q->queue);
   } else {
-    eq = event_queue_create(dest);
+    eq = event_queue_create(dest, outp);
     if(eq)
       queue_list_add(&queues, dest, eq);
 #if !defined(IL_NOTIFICATIONS)
@@ -171,7 +196,7 @@ queue_list_init(char *ls)
 {
 #if !defined(IL_NOTIFICATIONS)
   /* create queue for log server */
-  log_queue = event_queue_create(ls);
+  log_queue = event_queue_create(ls, NULL);
   if(log_queue == NULL)
     return(-1);
 #endif
diff --git a/org.glite.lb.logger/src/queue_mgr_http.c b/org.glite.lb.logger/src/queue_mgr_http.c
new file mode 100644 (file)
index 0000000..f5e767f
--- /dev/null
@@ -0,0 +1,181 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "glite/jobid/cjobid.h"
+#include "glite/lb/context.h"
+
+#include "interlogd.h"
+
+struct queue_list {
+  struct event_queue *queue;
+  char   *dest;
+  struct queue_list *next;
+  time_t expires;
+};
+
+static struct event_queue *log_queue;
+static struct queue_list  *queues;
+
+
+static 
+int
+queue_list_create()
+{
+  queues = NULL;
+
+  return(0);
+}
+
+
+static
+int
+queue_list_find(struct queue_list *ql, const char *dest, struct queue_list **el, struct queue_list **prev)
+{
+  struct queue_list *q, *p;
+
+  assert(el != NULL);
+
+  *el = NULL;
+  if(prev)
+    *prev = NULL;
+
+  if(ql == NULL) 
+    return(0);
+
+  q = NULL;
+  p = ql;
+
+  while(p) {
+    if(strcmp(p->dest, dest) == 0) {
+      *el = p;
+      if(prev)
+       *prev = q;
+      return(1);
+    }
+
+    q = p;
+    p = p->next;
+  };
+
+  return(0);
+}
+
+
+static
+int
+queue_list_add(struct queue_list **ql, const char *dest, struct event_queue *eq)
+{
+  struct queue_list *el;
+  
+  assert(dest != NULL);
+  assert(eq != NULL);
+  assert(ql != NULL);
+
+  el = malloc(sizeof(*el));
+  if(el == NULL) {
+    set_error(IL_NOMEM, ENOMEM, "queue_list_add: not enough room for new queue");
+    return(-1);
+  }
+
+  el->dest = strdup(dest);
+  if(el->dest == NULL) {
+    free(el);
+    set_error(IL_NOMEM, ENOMEM, "queue_list_add: not enough memory for new queue");
+    return(-1);
+  }
+  el->queue = eq;
+  el->next = *ql;
+  *ql = el;
+  return 0;
+}
+
+
+struct event_queue *
+queue_list_get(char *job_id_s)
+{
+  char *dest;
+  struct queue_list *q;
+  struct event_queue *eq;
+  dest = job_id_s;
+
+  if(dest == NULL) 
+    return(NULL);
+  
+  if(queue_list_find(queues, dest, &q, NULL)) {
+    return(q->queue);
+  } else {
+    eq = event_queue_create(dest);
+    if(eq)
+      queue_list_add(&queues, dest, eq);
+    return(eq);
+  }
+}
+
+
+int
+queue_list_is_log(struct event_queue *eq)
+{
+  return(eq == queue_list_get(NULL));
+}
+
+
+int
+queue_list_init(char *ls)
+{
+  return(queue_list_create());
+}
+
+
+static struct queue_list *current;
+
+
+struct event_queue *
+queue_list_first()
+{
+  current = queues;
+  return(current ? current->queue : NULL);
+}
+
+
+struct event_queue *
+queue_list_next()
+{
+  current = current ? current->next : NULL;
+  return(current ? current->queue : NULL);
+}
+
+
+int
+queue_list_remove_queue(struct event_queue *eq)
+{
+  assert(eq != NULL);
+
+  free(eq);
+  return(1);
+}
+
+
+
+/* Local Variables:           */
+/* c-indentation-style: gnu   */
+/* End:                       */
index 20c25e7..7817d75 100644 (file)
@@ -1,9 +1,27 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <stdio.h>
 #include <assert.h>
 #include <errno.h>
 #include <signal.h>
+#include <unistd.h>
 
 #include "interlogd.h"
 
@@ -13,7 +31,7 @@ queue_thread_cleanup(void *q)
 {
        struct event_queue *eq = (struct event_queue *)q;
 
-       il_log(LOG_WARNING, "thread %d exits\n", eq->thread_id);
+       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_WARN, "thread %d exits", eq->thread_id);
 
        /* unlock all held locks */
        /* FIXME: check that the thread always exits when holding these locks;
@@ -48,11 +66,14 @@ queue_thread(void *q)
        int exit_timeout = EXIT_TIMEOUT;
 
        if(init_errors(0) < 0) {
-               il_log(LOG_ERR, "Error initializing thread specific data, exiting!");
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR, 
+                                "Error initializing thread specific data, exiting!");
                pthread_exit(NULL);
        }
   
-       il_log(LOG_DEBUG, "  started new thread for delivery to %s:%d\n", eq->dest_name, eq->dest_port);
+       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, 
+                        "  started new thread for delivery to %s",
+                        eq->dest);
 
        pthread_cleanup_push(queue_thread_cleanup, q); 
 
@@ -74,23 +95,28 @@ queue_thread(void *q)
                        if(lazy_close && close_timeout) {
                                ret = event_queue_wait(eq, close_timeout);
                                if(ret == 1) {/* timeout? */
-                                       event_queue_close(eq);
-                                       il_log(LOG_DEBUG, "  connection to %s:%d closed\n",
-                                              eq->dest_name, eq->dest_port);
+                                       (*eq->event_queue_close)(eq);
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                                        "  connection to %s closed",
+                                                        eq->dest);
                                }
                                close_timeout = 0;
                        } else {
                                ret = event_queue_wait(eq, exit_timeout);
                                if(ret == 1) {
-                                       il_log(LOG_INFO, "  thread idle for more than %d seconds, exiting\n", exit_timeout);
-                                       event_queue_close(eq);
+                                       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_INFO, 
+                                                        "  thread idle for more than %d seconds, exiting", 
+                                                        exit_timeout);
+                                       (*eq->event_queue_close)(eq);
                                        event_queue_cond_unlock(eq);
                                        pthread_exit((void*)0);
                                }
                        }
                        if(ret < 0) {
                                /* error waiting */
-                               il_log(LOG_ERR, "queue_thread: %s\n", error_get_msg());
+                               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_WARN, 
+                                                "queue_thread: %s", 
+                                                error_get_msg());
                                event_queue_cond_unlock(eq);
                                pthread_exit((void*)-1);
                        }
@@ -103,44 +129,58 @@ queue_thread(void *q)
                event_queue_cond_unlock(eq);
                
                /* discard expired events */
-               il_log(LOG_DEBUG, "  discarding expired events\n");
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, "  discarding expired events");
                now = time(NULL);
                event_queue_move_events(eq, NULL, cmp_expires, &now);
                if(!event_queue_empty(eq)) {
 
                        /* deliver pending events */
-                       il_log(LOG_DEBUG, "  attempting delivery to %s:%d\n", eq->dest_name, eq->dest_port);
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                        "  attempting delivery to %s",
+                                        eq->dest);
                        /* connect to server */
-                       if((ret=event_queue_connect(eq)) == 0) {
+                       if((ret=(*eq->event_queue_connect)(eq)) == 0) {
                                /* not connected */
                                if(error_get_maj() != IL_OK)
-                                       il_log(LOG_ERR, "queue_thread: %s\n", error_get_msg());
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                                                        "queue_thread: %s", error_get_msg());
 #if defined(IL_NOTIFICATIONS)
-                               il_log(LOG_INFO, "    could not connect to client %s, waiting for retry\n", eq->dest_name);
+                               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                                                "    could not connect to client %s, waiting for retry", 
+                                                eq->dest);
 #else
-                               il_log(LOG_INFO, "    could not connect to bookkeeping server %s, waiting for retry\n", eq->dest_name);
+                               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_INFO, 
+                                                "    could not connect to bookkeeping server %s, waiting for retry", 
+                                                eq->dest);
 #endif
                                retrycnt++;
                        } else {
                                retrycnt = 0;
                                /* connected, send events */
-                               switch(ret=event_queue_send(eq)) {
+                               switch(ret=(*eq->event_queue_send)(eq)) {
                                        
                                case 0:
                                        /* there was an error and we still have events to send */
                                        if(error_get_maj() != IL_OK)
-                                               il_log(LOG_ERR, "queue_thread: %s\n", error_get_msg());
-                                       il_log(LOG_DEBUG, "  events still waiting\n");
+                                               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, 
+                                                                "queue_thread: %s", 
+                                                                error_get_msg());
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                                        "  events still waiting");
                                        break;
                                        
                                case 1:
                                        /* hey, we are done for now */
-                                       il_log(LOG_DEBUG, "  all events for %s sent\n", eq->dest_name);
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                                        "  all events for %s sent", 
+                                                        eq->dest);
                                        break;
                                        
                                default:
                                        /* internal error */
-                                       il_log(LOG_ERR, "queue_thread: %s\n", error_get_msg());
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                                                        "queue_thread: %s", 
+                                                        error_get_msg());
                                        exit = 1;      
                                        break;
                                        
@@ -150,9 +190,10 @@ queue_thread(void *q)
                                if((ret == 1) && lazy_close)
                                        close_timeout = default_close_timeout;
                                else {
-                                       event_queue_close(eq);
-                                       il_log(LOG_DEBUG, "  connection to %s:%d closed\n",
-                                              eq->dest_name, eq->dest_port);
+                                       (*eq->event_queue_close)(eq);
+                                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG,
+                                                        "  connection to %sclosed",
+                                                        eq->dest);
                                }
                        }
                } 
@@ -164,7 +205,8 @@ queue_thread(void *q)
 
                /* Check if we are flushing and if we are, report status to master */
                if(eq->flushing == 1) {
-                       il_log(LOG_DEBUG, "    flushing mode detected, reporting status\n");
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                        "    flushing mode detected, reporting status");
                        /* 0 - events waiting, 1 - events sent, < 0 - some error */
                        eq->flush_result = ret;
                        eq->flushing = 2;
@@ -182,7 +224,8 @@ queue_thread(void *q)
                   which may cure server kicking us out after given number of connections */
 #ifndef LB_PERF
                if((ret == 0) && (retrycnt > 0)) {
-                       il_log(LOG_WARNING, "    sleeping\n");
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                        "    sleeping");
                        event_queue_sleep(eq);
                }
 #endif
@@ -208,18 +251,22 @@ queue_thread(void *q)
 int
 event_queue_create_thread(struct event_queue *eq)
 {
+       pthread_attr_t attr;
+
        assert(eq != NULL);
 
        event_queue_lock(eq);
 
        /* if there is a thread already, just return */
-       if(eq->thread_id > 0) {
+       if(eq->thread_id != 0) {
                event_queue_unlock(eq);
                return(0);
        }
 
        /* create the thread itself */
-       if(pthread_create(&eq->thread_id, NULL, queue_thread, eq) < 0) {
+       pthread_attr_init(&attr);
+       pthread_attr_setstacksize(&attr, 16384);
+       if(pthread_create(&eq->thread_id, &attr, queue_thread, eq) < 0) {
                eq->thread_id = 0;
                set_error(IL_SYS, errno, "event_queue_create_thread: error creating new thread");
                event_queue_unlock(eq);
index 981b2b6..250a40a 100644 (file)
@@ -1,6 +1,24 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <stdio.h>
+#include <unistd.h>
 #include <assert.h>
 #include <errno.h>
 
@@ -14,42 +32,61 @@ void *
 recover_thread(void *q)
 {
        if(init_errors(0) < 0) {
-               il_log(LOG_ERR, "Error initializing thread specific data, exiting!");
+               glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_ERROR, 
+                                "Error initializing thread specific data, exiting!");
                pthread_exit(NULL);
        }
 
        while(1) {
-               il_log(LOG_INFO, "Looking up event files...\n");
+               glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                                "Looking up event files.");
                if(event_store_init(file_prefix) < 0) {
-                       il_log(LOG_ERR, "recover_thread: %s\n", error_get_msg());
+                       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, 
+                                        "recover_thread: %s", error_get_msg());
                        exit(1);
                }
                if(event_store_recover_all() < 0) {
-                       il_log(LOG_ERR, "recover_thread: %s\n", error_get_msg());
+                       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, 
+                                        "recover_thread: %s", error_get_msg());
                        exit(1);
                }
                if(event_store_cleanup() < 0) {
-                       il_log(LOG_ERR, "recover_thread: %s\n", error_get_msg());
+                       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, 
+                                        "recover_thread: %s", error_get_msg());
                        exit(1);
                }
-               il_log(LOG_INFO, "Reloading certificate...\n");
-               if(pthread_mutex_lock(&cred_handle_lock) < 0)
-                       abort();
-               {
-                       gss_cred_id_t new_cred_handle = GSS_C_NO_CREDENTIAL;
-                       OM_uint32 min_stat;
+               glite_common_log(LOG_CATEGORY_SECURITY, LOG_PRIORITY_DEBUG, "Checking for new certificate.");
+               if (edg_wll_gss_watch_creds(cert_file, &cert_mtime) > 0) {
+                       edg_wll_GssCred new_creds = NULL;
                        int ret;
 
                        ret = edg_wll_gss_acquire_cred_gsi(cert_file,key_file, 
-                               &new_cred_handle, NULL, NULL);
-                       if (new_cred_handle != GSS_C_NO_CREDENTIAL) {
-                               gss_release_cred(&min_stat, &cred_handle);
-                               cred_handle = new_cred_handle;
-                               il_log(LOG_INFO, "New certificate found and deployed.\n");
+                               &new_creds, NULL);
+                       if (new_creds != NULL) {
+                               if(pthread_mutex_lock(&cred_handle_lock) < 0)
+                                       abort();
+                               /* if no one is using the old credentials, release them */
+                               if(cred_handle && cred_handle->counter == 0) {
+                                       edg_wll_gss_release_cred(&cred_handle->creds, NULL);
+                                       free(cred_handle);
+                                       glite_common_log(LOG_CATEGORY_SECURITY, LOG_PRIORITY_DEBUG, 
+                                                        "  freed old credentials");
+                               }
+                               cred_handle = malloc(sizeof(*cred_handle));
+                               if(cred_handle == NULL) {
+                                       glite_common_log(LOG_CATEGORY_CONTROL, LOG_PRIORITY_FATAL, 
+                                                        "Failed to allocate structure for credentials.");
+                                       exit(EXIT_FAILURE);
+                               }
+                               cred_handle->creds = new_creds;
+                               cred_handle->counter = 0;
+                               if(pthread_mutex_unlock(&cred_handle_lock) < 0)
+                                       abort();
+                               glite_common_log(LOG_CATEGORY_SECURITY, LOG_PRIORITY_INFO, 
+                                                "New certificate %s found and deployed.",
+                                                new_creds->name);
                        }
                }
-               if(pthread_mutex_unlock(&cred_handle_lock) < 0)
-                       abort();
-               sleep(INPUT_TIMEOUT);
+               sleep(RECOVER_TIMEOUT);
        }
 }
index 761b300..b48ab23 100644 (file)
@@ -1,10 +1,26 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <assert.h>
 #include <errno.h>
-#ifdef HAVE_UNISTD_H
+#include <stdio.h>
 #include <unistd.h>
-#endif
 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -15,7 +31,7 @@
  *   - L/B server protocol handling routines 
  */
 
-#include "glite/wmsutils/jobid/cjobid.h"
+#include "glite/jobid/cjobid.h"
 #include "glite/lb/il_string.h"
 #include "glite/lb/context.h"
 
@@ -59,7 +75,9 @@ send_confirmation(long lllid, int code)
   }
   ret = 1;
 
-  il_log(LOG_DEBUG, "  sent code %d back to client\n", code);
+  glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                  "  sent code %d back to client", 
+                  code);
 
  out:
   close(sock);
@@ -102,7 +120,8 @@ static
 int
 gss_reader(void *user_data, char *buffer, int max_len)
 {
-  int ret, len;
+  int ret;
+  size_t len;
   struct reader_data *data = (struct reader_data *)user_data;
   edg_wll_GssStatus gss_stat;
 
@@ -132,7 +151,7 @@ get_reply(struct event_queue *eq, char **buf, int *code_min)
 {
   char *msg=NULL;
   int ret, code;
-  int len, l;
+  int len;
   struct timeval tv;
   struct reader_data data;
 
@@ -165,6 +184,7 @@ event_queue_connect(struct event_queue *eq)
   int ret;
   struct timeval tv;
   edg_wll_GssStatus gss_stat;
+  cred_handle_t *local_cred_handle;
 
   assert(eq != NULL);
 
@@ -172,16 +192,35 @@ event_queue_connect(struct event_queue *eq)
   if(!nosend) {
 #endif
 
-  if(eq->gss.context == GSS_C_NO_CONTEXT) {
+  if(eq->gss.context == NULL) {
 
     tv.tv_sec = TIMEOUT;
     tv.tv_usec = 0;
+
+    /* get pointer to the credentials */
     if(pthread_mutex_lock(&cred_handle_lock) < 0)
            abort();
-    il_log(LOG_DEBUG, "    trying to connect to %s:%d\n", eq->dest_name, eq->dest_port);
-    ret = edg_wll_gss_connect(cred_handle, eq->dest_name, eq->dest_port, &tv, &eq->gss, &gss_stat);
+    local_cred_handle = cred_handle;
+    local_cred_handle->counter++;
     if(pthread_mutex_unlock(&cred_handle_lock) < 0)
            abort();
+    
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "    trying to connect to %s:%d", 
+                    eq->dest_name, eq->dest_port);
+    ret = edg_wll_gss_connect(local_cred_handle->creds, eq->dest_name, eq->dest_port, &tv, &eq->gss, &gss_stat);
+    if(pthread_mutex_lock(&cred_handle_lock) < 0)
+           abort();
+    /* check if we need to release the credentials */
+    --local_cred_handle->counter;
+    if(local_cred_handle != cred_handle && local_cred_handle->counter == 0) {
+           edg_wll_gss_release_cred(&local_cred_handle->creds, NULL);
+           free(local_cred_handle);
+           glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, "   freed credentials, not used anymore");
+    }
+    if(pthread_mutex_unlock(&cred_handle_lock) < 0) 
+           abort();
+
     if(ret < 0) {
       char *gss_err = NULL;
 
@@ -190,10 +229,11 @@ event_queue_connect(struct event_queue *eq)
       set_error(IL_DGGSS, ret,
                (ret == EDG_WLL_GSS_ERROR_GSS) ? gss_err : "event_queue_connect: edg_wll_gss_connect");
       if (gss_err) free(gss_err);
-      eq->gss.context = GSS_C_NO_CONTEXT;
+      eq->gss.context = NULL;
       eq->timeout = TIMEOUT;
       return(0);
     }
+    eq->first_event_sent = 0;
   }
 
 #ifdef LB_PERF
@@ -213,10 +253,11 @@ event_queue_close(struct event_queue *eq)
   if(!nosend) {
 #endif
 
-  if(eq->gss.context != GSS_C_NO_CONTEXT) {
+  if(eq->gss.context != NULL) {
     edg_wll_gss_close(&eq->gss, NULL);
-    eq->gss.context = GSS_C_NO_CONTEXT;
+    eq->gss.context = NULL;
   }
+  eq->first_event_sent = 0;
 #ifdef LB_PERF
   }
 #endif
@@ -231,13 +272,12 @@ event_queue_close(struct event_queue *eq)
 int 
 event_queue_send(struct event_queue *eq)
 {
-  int events_sent = 0;
   assert(eq != NULL);
 
 #ifdef LB_PERF
   if(!nosend) {
 #endif
-  if(eq->gss.context == GSS_C_NO_CONTEXT)
+  if(eq->gss.context == NULL)
     return(0);
 #ifdef LB_PERF
   }
@@ -257,23 +297,19 @@ event_queue_send(struct event_queue *eq)
     if(event_queue_get(eq, &msg) < 0) 
       return(-1);
 
-    il_log(LOG_DEBUG, "    trying to deliver event at offset %d for job %s\n", msg->offset, msg->job_id_s);
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "    trying to deliver event at offset %d for job %s", 
+                    msg->offset, msg->job_id_s);
 
 #ifdef LB_PERF
     if(!nosend) {
 #endif
-      if(msg->len) {
+       if (msg->len) {
            tv.tv_sec = TIMEOUT;
            tv.tv_usec = 0;
            ret = edg_wll_gss_write_full(&eq->gss, msg->msg, msg->len, &tv, &bytes_sent, &gss_stat);
-           /* commented out due to the conflict with following ljocha's code
            if(ret < 0) {
-                   eq->timeout = TIMEOUT;
-                   return(0);
-           }
-           */
-           if(ret < 0) {
-             if (ret == EDG_WLL_GSS_ERROR_ERRNO && errno == EPIPE && events_sent > 0)
+             if (ret == EDG_WLL_GSS_ERROR_ERRNO && errno == EPIPE && eq->first_event_sent )
                eq->timeout = 0;
              else
                eq->timeout = TIMEOUT;
@@ -282,18 +318,19 @@ event_queue_send(struct event_queue *eq)
            
            if((code = get_reply(eq, &rep, &code_min)) < 0) {
                    /* could not get the reply properly, so try again later */
-                   if (events_sent>0) {
+                   if (eq->first_event_sent) {
                        /* could be expected server connection preemption */
                        clear_error();
                        eq->timeout = 1;
                    } else {
                        eq->timeout = TIMEOUT;
-                       il_log(LOG_ERR, "  error reading server %s reply:\n    %s\n", eq->dest_name, error_get_msg());
+                       glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_WARN, "  error reading server %s reply: %s", 
+                                        eq->dest_name, error_get_msg());
                     }
                    return(0);
            }
-      } 
-      else { code = LB_OK; code_min = 0; rep = strdup("not sending empty message"); }
+       }
+       else { code = LB_OK; code_min = 0; rep = strdup("not sending empty message"); }
 #ifdef LB_PERF
     } else {
            glite_wll_perftest_consumeEventIlMsg(msg->msg+17);
@@ -302,7 +339,9 @@ event_queue_send(struct event_queue *eq)
     }
 #endif
     
-    il_log(LOG_DEBUG, "    event sent, server %s replied with %d, %s\n", eq->dest_name, code, rep);
+    glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_DEBUG, 
+                    "    event sent, server %s replied with %d, %s", 
+                    eq->dest_name, code, rep);
     free(rep);
 
     /* the reply is back here */
@@ -312,6 +351,8 @@ event_queue_send(struct event_queue *eq)
     case LB_NOMEM:
            /* NOT USED: case LB_SYS:  */
            /* NOT USED: case LB_AUTH: */
+       case LB_PERM:
+       case LB_DBERR:
       /* non fatal errors (for us) */
       eq->timeout = TIMEOUT;
       return(0);
@@ -319,10 +360,10 @@ event_queue_send(struct event_queue *eq)
     case LB_OK:
       /* event succesfully delivered */
       
-    default: /* LB_DBERR, LB_PROTO */
+    default: /* LB_PROTO */
       /* the event was not accepted by the server */
       /* update the event pointer */
-      if(event_store_commit(msg->es, msg->ev_len, queue_list_is_log(eq)) < 0) 
+        if(event_store_commit(msg->es, msg->ev_len, queue_list_is_log(eq), msg->generation) < 0)
        /* failure committing message, this is bad */
        return(-1);
       /* if we have just delivered priority message from the queue, send confirmation */
@@ -335,10 +376,12 @@ event_queue_send(struct event_queue *eq)
 
       if((ret == 0) &&
         (error_get_maj() != IL_OK))
-         il_log(LOG_ERR, "send_event: %s\n", error_get_msg());
+             glite_common_log(IL_LOG_CATEGORY, LOG_PRIORITY_ERROR, 
+                              "send_event: %s", 
+                              error_get_msg());
        
       event_queue_remove(eq);
-      events_sent++;
+      eq->first_event_sent = 1;
       break;
       
     } /* switch */
diff --git a/org.glite.lb.logger/src/send_event_http.c b/org.glite.lb.logger/src/send_event_http.c
new file mode 100644 (file)
index 0000000..ce79f73
--- /dev/null
@@ -0,0 +1,299 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+
+/*
+ *   - L/B server protocol handling routines 
+ */
+
+#include "glite/jobid/cjobid.h"
+#include "glite/lb/il_string.h"
+#include "glite/lb/context.h"
+
+#include "interlogd.h"
+
+struct reader_data {
+       edg_wll_GssConnection *gss;
+       struct timeval *timeout;
+};
+
+
+static
+int
+gss_reader(void *user_data, char *buffer, int max_len)
+{
+  int ret;
+  struct reader_data *data = (struct reader_data *)user_data;
+  edg_wll_GssStatus gss_stat;
+
+  ret = edg_wll_gss_read(data->gss, buffer, max_len, data->timeout, &gss_stat);
+  if(ret < 0) {
+    char *gss_err = NULL;
+
+    if(ret == EDG_WLL_GSS_ERROR_GSS) {
+      edg_wll_gss_get_error(&gss_stat, "get_reply", &gss_err);
+      set_error(IL_DGGSS, ret, gss_err);
+      free(gss_err);
+    } else 
+      set_error(IL_DGGSS, ret, "get_reply");
+  }
+  return(ret);
+}
+
+
+/*
+ * Read reply from server.
+ *  Returns: -1 - error reading message, 
+ *         code > 0 - http status code from server
+ */
+static
+int 
+get_reply(struct event_queue *eq, char **buf, int *code_min)
+{
+  int ret, code;
+  int len;
+  struct timeval tv;
+  struct reader_data data;
+  il_http_message_t msg;
+
+  tv.tv_sec = TIMEOUT;
+  tv.tv_usec = 0;
+  data.gss = &eq->gss;
+  data.timeout = &tv;
+  len = receive_http(&data, gss_reader, &msg);
+  if(len < 0) {
+    set_error(IL_PROTO, LB_PROTO, "get_reply: error reading server reply");
+    return(-1);
+  }
+  if(msg.data) free(msg.data);
+  if(msg.reply_string) *buf = msg.reply_string;
+  *code_min = 0; /* XXX fill in flag for fault */
+  return(msg.reply_code);
+}
+
+
+
+/*
+ *  Returns: 0 - not connected, timeout set, 1 - OK
+ */
+int 
+event_queue_connect(struct event_queue *eq)
+{
+  int ret;
+  struct timeval tv;
+  edg_wll_GssStatus gss_stat;
+  cred_handle_t *local_cred_handle;
+
+  assert(eq != NULL);
+
+#ifdef LB_PERF
+  if(!nosend) {
+#endif
+
+  if(eq->gss.context == NULL) {
+
+    tv.tv_sec = TIMEOUT;
+    tv.tv_usec = 0;
+
+    /* get pointer to the credentials */
+    if(pthread_mutex_lock(&cred_handle_lock) < 0)
+           abort();
+    local_cred_handle = cred_handle;
+    local_cred_handle->counter++;
+    if(pthread_mutex_unlock(&cred_handle_lock) < 0)
+           abort();
+    
+    il_log(LOG_DEBUG, "    trying to connect to %s:%d\n", eq->dest_name, eq->dest_port);
+    ret = edg_wll_gss_connect(local_cred_handle->creds, eq->dest_name, eq->dest_port, &tv, &eq->gss, &gss_stat);
+    if(pthread_mutex_lock(&cred_handle_lock) < 0)
+           abort();
+    /* check if we need to release the credentials */
+    --local_cred_handle->counter;
+    if(local_cred_handle != cred_handle && local_cred_handle->counter == 0) {
+           edg_wll_gss_release_cred(&local_cred_handle->creds, NULL);
+           free(local_cred_handle);
+           il_log(LOG_DEBUG, "   freed credentials, not used anymore\n");
+    }
+    if(pthread_mutex_unlock(&cred_handle_lock) < 0) 
+           abort();
+
+    if(ret < 0) {
+      char *gss_err = NULL;
+
+      if (ret == EDG_WLL_GSS_ERROR_GSS)
+        edg_wll_gss_get_error(&gss_stat, "event_queue_connect: edg_wll_gss_connect", &gss_err);
+      set_error(IL_DGGSS, ret,
+               (ret == EDG_WLL_GSS_ERROR_GSS) ? gss_err : "event_queue_connect: edg_wll_gss_connect");
+      if (gss_err) free(gss_err);
+      eq->gss.context = NULL;
+      eq->timeout = TIMEOUT;
+      return(0);
+    }
+  }
+
+#ifdef LB_PERF
+  }
+#endif
+
+  return(1);
+}
+
+
+int
+event_queue_close(struct event_queue *eq)
+{
+  assert(eq != NULL);
+
+#ifdef LB_PERF
+  if(!nosend) {
+#endif
+
+  if(eq->gss.context != NULL) {
+    edg_wll_gss_close(&eq->gss, NULL);
+    eq->gss.context = NULL;
+  }
+#ifdef LB_PERF
+  }
+#endif
+  return(0);
+}
+
+
+/* 
+ * Send all events from the queue.
+ *   Returns: -1 - system error, 0 - not sent, 1 - queue empty
+ */
+int 
+event_queue_send(struct event_queue *eq)
+{
+  int events_sent = 0;
+  assert(eq != NULL);
+
+#ifdef LB_PERF
+  if(!nosend) {
+#endif
+  if(eq->gss.context == NULL)
+    return(0);
+#ifdef LB_PERF
+  }
+#endif
+
+  /* feed the server with events */
+  while (!event_queue_empty(eq)) {
+    struct server_msg *msg;
+    char *rep;
+    int  ret, code, code_min;
+    size_t bytes_sent;
+    struct timeval tv;
+    edg_wll_GssStatus gss_stat;
+
+    clear_error();
+
+    if(event_queue_get(eq, &msg) < 0) 
+      return(-1);
+
+    il_log(LOG_DEBUG, "    trying to deliver event at offset %d for job %s\n", msg->offset, msg->job_id_s);
+
+#ifdef LB_PERF
+    if(!nosend) {
+#endif
+        /* XXX: ljocha -- does it make sense to send empty messages ? */
+       if (msg->len) {
+           tv.tv_sec = TIMEOUT;
+           tv.tv_usec = 0;
+           ret = edg_wll_gss_write_full(&eq->gss, msg->msg, msg->len, &tv, &bytes_sent, &gss_stat);
+           if(ret < 0) {
+                   if (ret == EDG_WLL_GSS_ERROR_ERRNO && errno == EPIPE && events_sent > 0) {
+                           eq->timeout = 0;
+                   }  else {
+                           il_log(LOG_ERR, "send_event: %s\n", error_get_msg());
+                           eq->timeout = TIMEOUT;
+                   }
+                   return(0);
+           }
+           if((code = get_reply(eq, &rep, &code_min)) < 0) {
+                   /* could not get the reply properly, so try again later */
+                   if (events_sent>0) 
+                           eq->timeout = 1;
+                   else {
+                           eq->timeout = TIMEOUT;
+                           il_log(LOG_ERR, "  error reading server %s reply:\n    %s\n", eq->dest_name, error_get_msg());
+                   }
+                   return(0);
+           }
+       }
+       else { code = 200; code_min = 0; rep = strdup("not sending empty message"); }
+#ifdef LB_PERF
+    } else {
+           glite_wll_perftest_consumeEventIlMsg(msg->msg+17);
+           code = 200;
+           rep = strdup("OK");
+    }
+#endif
+    
+    il_log(LOG_DEBUG, "    event sent, server %s replied with %d, %s\n", eq->dest_name, code, rep);
+    free(rep);
+
+    /* the reply is back here, decide what to do with message */
+    /* HTTP error codes:
+       1xx - informational (eg. 100 Continue)
+       2xx - successful (eg. 200 OK)
+       3xx - redirection (eg. 301 Moved Permanently)
+       4xx - client error (eq. 400 Bad Request)
+       5xx - server error (eq. 500 Internal Server Error)
+    */
+    if(code >= 100 && code < 200) {
+
+           /* non fatal errors (for us), try to deliver later */
+           eq->timeout = TIMEOUT;
+           return(0);
+    }
+
+    /* the message was consumed (successfully or not) */
+    /* update the event pointer */
+    if(event_store_commit(msg->es, msg->ev_len, queue_list_is_log(eq)) < 0) 
+           /* failure committing message, this is bad */
+           return(-1);
+    
+    event_queue_remove(eq);
+    events_sent++;
+  } /* while */
+
+  return(1);
+
+} /* send_events */
+
+
+/* this is just not used */
+int
+send_confirmation(long lllid, int code)
+{
+       return 0;
+}
index e9578a1..aa57e1a 100644 (file)
@@ -1,13 +1,30 @@
 #ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 
 #include <errno.h>
 #include <assert.h>
 #include <string.h>
+#include <stdio.h>
 
 #include "interlogd.h"
 #include "glite/lb/il_msg.h" 
 #include "glite/lb/events_parse.h"
-#include "glite/lb/consumer.h"
 #include "glite/lb/context.h"
 
 static
@@ -17,7 +34,7 @@ create_msg(il_octet_string_t *ev, char **buffer, long *receipt, time_t *expires)
   char *p;  int  len;
   char *event = ev->data;
 
-  *receipt = 0;
+  *receipt = 0L;
 
 #if defined(INTERLOGD_EMS)
   /* find DG.LLLID */
@@ -42,24 +59,23 @@ create_msg(il_octet_string_t *ev, char **buffer, long *receipt, time_t *expires)
       int n;
       
       p += 12; /* skip the key and = */
-      if((n = atoi(p)) == 0) {
+      n = atoi(p);
+      if((n & (EDG_WLL_LOGFLAG_SYNC|EDG_WLL_LOGFLAG_SYNC_COMPAT)) == 0) {
        /* normal asynchronous message */
-       *receipt = 0;
+             *receipt = 0L;
       }
     } else {
       /* could not find priority key */
-      *receipt = 0;
+      *receipt = 0L;
     }
     
   } else {
     /* could not find local logger PID, confirmation can not be sent */
-    *receipt = 0;
+    *receipt = 0L;
   }
 #endif
 
-  if(p = strstr(event, "DG.EXPIRES")) {
-         int n;
-
+  if((p = strstr(event, "DG.EXPIRES")) != NULL) {
          p += 11;
          *expires = atoi(p);
   }
@@ -119,11 +135,12 @@ server_msg_copy(struct server_msg *src)
   msg->receipt_to = src->receipt_to;
   msg->offset = src->offset;
 #if defined(IL_NOTIFICATIONS)
-  msg->dest_name = strdup(src->dest_name);
+  msg->dest_name = src->dest_name ? strdup(src->dest_name) : NULL;
   msg->dest_port = src->dest_port;
-  msg->dest = strdup(src->dest);
+  msg->dest = src->dest ? strdup(src->dest) : NULL;
 #endif
   msg->expires = src->expires;
+  msg->generation = src->generation;
   return(msg);
 }
 
@@ -144,16 +161,26 @@ server_msg_init(struct server_msg *msg, il_octet_string_t *event)
 
 
 #if defined(IL_NOTIFICATIONS)
-       edg_wll_InitContext(&context);
 
        /* parse the notification event */
-       if((ret=edg_wll_ParseNotifEvent(context, event->data, &notif_event))) {
+       edg_wll_InitContext(&context);
+       ret=edg_wll_ParseNotifEvent(context, event->data, &notif_event);
+       edg_wll_FreeContext(context);
+       if(ret) {
                set_error(IL_LBAPI, ret, "server_msg_init: error parsing notification event");
                return(-1);
        }
+
        /* FIXME: check for allocation error */
-       if(notif_event->notification.dest_host && 
+       if(notif_event->notification.dest_url &&
+               (strlen(notif_event->notification.dest_url) > 0)) {
+               /* destination URL */
+               msg->dest = strdup(notif_event->notification.dest_url);
+               msg->dest_name = NULL;
+               msg->dest_port = 0;
+       } else if(notif_event->notification.dest_host &&
           (strlen(notif_event->notification.dest_host) > 0)) {
+               /* destination host and port */
                msg->dest_name = strdup(notif_event->notification.dest_host);
                msg->dest_port = notif_event->notification.dest_port;
                asprintf(&msg->dest, "%s:%d", msg->dest_name, msg->dest_port);
diff --git a/org.glite.lb.logger/src/server_msg_http.c b/org.glite.lb.logger/src/server_msg_http.c
new file mode 100644 (file)
index 0000000..3a0209a
--- /dev/null
@@ -0,0 +1,145 @@
+#ident "$Header$"
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+
+#include "interlogd.h"
+#include "glite/lb/il_msg.h" 
+#include "glite/lb/events_parse.h"
+#include "glite/lb/context.h"
+
+static
+int 
+create_msg(il_http_message_t *ev, char **buffer, long *receipt, time_t *expires)
+{
+  char *event = ev->data;
+
+  *receipt = 0;
+  *expires = 0;
+
+  *buffer = ev->data;
+  return ev->len;;
+}
+
+
+struct server_msg *
+server_msg_create(il_octet_string_t *event, long offset)
+{
+  struct server_msg *msg;
+
+  msg = malloc(sizeof(*msg));
+  if(msg == NULL) {
+    set_error(IL_NOMEM, ENOMEM, "server_msg_create: out of memory allocating message");
+    return(NULL);
+  }
+
+  if(server_msg_init(msg, event) < 0) {
+    server_msg_free(msg);
+    return(NULL);
+  }
+  msg->offset = offset;
+
+  return(msg);
+}
+
+
+struct server_msg *
+server_msg_copy(struct server_msg *src)
+{
+  struct server_msg *msg;
+
+  msg = malloc(sizeof(*msg));
+  if(msg == NULL) {
+    set_error(IL_NOMEM, ENOMEM, "server_msg_copy: out of memory allocating message");
+    return(NULL);
+  }
+  
+  msg->msg = malloc(src->len);
+  if(msg->msg == NULL) {
+    set_error(IL_NOMEM, ENOMEM, "server_msg_copy: out of memory allocating server message");
+    server_msg_free(msg);
+    return(NULL);
+  }
+  msg->len = src->len;
+  memcpy(msg->msg, src->msg, src->len);
+
+  msg->job_id_s = strdup(src->job_id_s);
+  msg->ev_len = src->ev_len;
+  msg->es = src->es;
+  msg->receipt_to = src->receipt_to;
+  msg->offset = src->offset;
+#if defined(IL_NOTIFICATIONS)
+  msg->dest_name = strdup(src->dest_name);
+  msg->dest_port = src->dest_port;
+  msg->dest = strdup(src->dest);
+#endif
+  msg->expires = src->expires;
+  return(msg);
+}
+
+
+int
+server_msg_init(struct server_msg *msg, il_octet_string_t *event)
+{
+       il_http_message_t *hmsg = (il_http_message_t *)event;
+
+       assert(msg != NULL);
+       assert(event != NULL);
+
+       memset(msg, 0, sizeof(*msg));
+
+
+       msg->job_id_s = hmsg->host;
+       if(msg->job_id_s == NULL) {
+               set_error(IL_LBAPI, EDG_WLL_ERROR_PARSE_BROKEN_ULM, "server_msg_init: error getting id");
+               return -1;
+       }
+       msg->len = create_msg(hmsg, &msg->msg, &msg->receipt_to, &msg->expires);
+       if(msg->len < 0)
+               return -1;
+       /* set this to indicate new data owner */
+       hmsg->data = NULL;
+       hmsg->host = NULL;
+       msg->ev_len = hmsg->len + 1; /* must add separator size too */
+       return 0;
+
+}
+
+
+int
+server_msg_is_priority(struct server_msg *msg)
+{
+  assert(msg != NULL);
+
+  return(msg->receipt_to != 0);
+}
+
+
+int
+server_msg_free(struct server_msg *msg)
+{
+  assert(msg != NULL);
+
+  if(msg->msg) free(msg->msg);
+  if(msg->job_id_s) free(msg->job_id_s);
+  free(msg);
+  return 0;
+}
index a6b4624..4ef7301 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include "IlTestBase.h"
 
 #include <string.h>
index 69bf347..449dbee 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 extern "C" {
 #include "interlogd.h"
 }
index 86a6be1..e07c19f 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
  
 #include "IlTestBase.h"
index 8364cf4..982c7ff 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
  
 #include "IlTestBase.h"
index cd97a73..3d1d554 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/TestFactoryRegistry.h>
 #include <cppunit/ui/text/TestRunner.h>
 
index d29e2cc..dd53f52 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
  
 #include "IlTestBase.h"
index 1e8dce6..1ac340a 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <iostream>
 
 #include <cppunit/extensions/HelperMacros.h>
index 9ee0b44..fcd78cf 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/un.h>
index e669ea1..2bdc50d 100644 (file)
@@ -1,3 +1,20 @@
+/*
+Copyright (c) Members of the EGEE Collaboration. 2004-2010.
+See http://www.eu-egee.org/partners for details on the copyright holders.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 #include <cppunit/extensions/HelperMacros.h>
  
 #include "IlTestBase.h"