From a78c33c5ec7c3885789b976b71589b8921ab2caa Mon Sep 17 00:00:00 2001 From: cvs2svn Date: Mon, 31 Oct 2005 11:08:32 +0000 Subject: [PATCH] This commit was manufactured by cvs2svn to create tag 'GLITE_RELEASE_1_4_1'. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Sprout from master 2005-08-08 14:17:20 UTC Aleš Křenek 'clean up install: tools to sbin, examples to examples' Cherrypick from master 2005-10-12 12:20:59 UTC Andrew McNab 'Older OpenSSL fix': org.gridsite.core/CHANGES org.gridsite.core/INSTALL org.gridsite.core/README org.gridsite.core/VERSION org.gridsite.core/doc/delegation-1.wsdl org.gridsite.core/doc/findproxyfile.1 org.gridsite.core/doc/gsexec.8 org.gridsite.core/doc/htcp.1 org.gridsite.core/doc/htfind.1 org.gridsite.core/doc/htmv.1 org.gridsite.core/doc/htping.1 org.gridsite.core/doc/httpd-fileserver.conf org.gridsite.core/doc/httpd-webserver.conf org.gridsite.core/doc/index.html org.gridsite.core/doc/mod_gridsite.8 org.gridsite.core/doc/urlencode.1 org.gridsite.core/interface/gridsite.h org.gridsite.core/project/version.properties org.gridsite.core/src/Makefile org.gridsite.core/src/gridsite.spec org.gridsite.core/src/grst_asn1.c org.gridsite.core/src/grst_gacl.c org.gridsite.core/src/grst_htcp.c org.gridsite.core/src/grst_http.c org.gridsite.core/src/grst_x509.c org.gridsite.core/src/gsexec.c org.gridsite.core/src/htcp org.gridsite.core/src/htcp.c org.gridsite.core/src/mod_gridsite.c org.gridsite.core/src/showx509exts.c Cherrypick from master 2004-08-17 13:41:21 UTC Elisabetta Ronchieri 'Moved out JobIdExceptions.h cjobid.h JobId.h': org.glite.security.proxyrenewal/project/configure.properties.xml org.glite.wms-utils.jobid/interface/glite/wmsutils/jobid/JobId.h Cherrypick from master 2005-02-15 09:29:04 UTC Jiří Škrábal '- files lb_gss.* renamed to glite_gss.*': org.glite.security.gsoap-plugin/test/test_gss.cpp Cherrypick from master 2005-08-11 12:04:48 UTC Aleš Křenek 'Tagged dependencies properties file [GLBUILDER]': org.glite.lb/project/dependencies.properties Cherrypick from glite-security-proxyrenewal_branch_1_0_0_RC1 2005-08-10 18:42:48 UTC Joni Hahkala 'get the header from teh right place after the removal of the copy': org.glite.security.proxyrenewal/Makefile org.glite.security.proxyrenewal/build.xml org.glite.security.proxyrenewal/config/startup org.glite.security.proxyrenewal/interface/renewal.h org.glite.security.proxyrenewal/project/build.number org.glite.security.proxyrenewal/project/version.properties org.glite.security.proxyrenewal/src/api.c org.glite.security.proxyrenewal/src/commands.c org.glite.security.proxyrenewal/src/common.c org.glite.security.proxyrenewal/src/renew.c org.glite.security.proxyrenewal/src/renewal_locl.h org.glite.security.proxyrenewal/src/renewd.c org.glite.security.proxyrenewal/src/renewd_locl.h org.glite.security.proxyrenewal/src/voms.c Cherrypick from glite-deployment-lb_branch_2_0_0 2005-10-31 11:08:31 UTC Master Builder 'Incremented build number [GLBUILDER]': org.glite.deployment.lb/CHANGELOG org.glite.deployment.lb/config/scripts/glite-lb-config.py org.glite.deployment.lb/config/templates/glite-lb.cfg.xml org.glite.deployment.lb/doc/release_notes/release_notes.doc org.glite.deployment.lb/doc/release_notes/release_notes.html org.glite.deployment.lb/doc/release_notes/release_notes.pdf org.glite.deployment.lb/project/build.number org.glite.deployment.lb/project/glite-lb.sdf.xml.template org.glite.deployment.lb/project/lxscript-rpm.xsl org.glite.deployment.lb/project/quattor-template.xsl org.glite.deployment.lb/project/version.properties Cherrypick from glite-security-gsoap-plugin_branch_1_1_0 2005-06-02 11:00:51 UTC Jiří Škrábal '- clean connection on unsuccessfull connect': org.glite.security.gsoap-plugin/Makefile org.glite.security.gsoap-plugin/build.xml org.glite.security.gsoap-plugin/project/build.number org.glite.security.gsoap-plugin/project/configure.properties.xml org.glite.security.gsoap-plugin/project/version.properties org.glite.security.gsoap-plugin/src/glite_gsplugin.c org.glite.security.gsoap-plugin/src/stdsoap2_2.6.2.c Cherrypick from glite-wms-utils-exception_branch_1_0_0 2005-07-28 13:55:08 UTC Giuseppe Avellino 'updating version': org.glite.wms-utils.exception/build.xml org.glite.wms-utils.exception/configure.ac org.glite.wms-utils.exception/interface/glite/wmsutils/exception/Exception.h org.glite.wms-utils.exception/project/build.number org.glite.wms-utils.exception/project/version.properties org.glite.wms-utils.exception/src/Exception.cpp Cherrypick from glite-lb-server_branch_1_2_4 2005-08-11 12:01:36 UTC Aleš Křenek 'pushed version to reflect internal testing fixes': org.glite.lb.server/Makefile org.glite.lb.server/project/configure.properties.xml org.glite.lb.server/project/version.properties org.glite.lb.server/src/request.c Cherrypick from glite-lb-client_branch_2_0_0 2005-08-11 11:58:24 UTC Aleš Křenek 'pushed version to reflect internal testing fixes': org.glite.lb.client/project/version.properties org.glite.lb.client/src/prod_proto.c org.glite.lb.client/src/producer.c Cherrypick from glite-lb-common_branch_2_0_0 2005-08-11 11:57:43 UTC Aleš Křenek 'pushed version to reflect internal testing fixes': org.glite.lb.common/project/version.properties org.glite.lb.common/src/il_msg.c org.glite.lb.common/test/il_msg_test.cpp Cherrypick from glite-wms-utils-jobid_branch_1_0_0 2005-04-03 01:12:39 UTC Master Builder 'Incremented build number [GLBUILDER]': org.glite.wms-utils.jobid/build.xml org.glite.wms-utils.jobid/project/build.number org.glite.wms-utils.jobid/project/version.properties Cherrypick from glite-lb-logger_branch_1_1_1 2005-08-11 12:00:24 UTC Aleš Křenek 'pushed version to reflect internal testing fixes': org.glite.lb.logger/project/version.properties org.glite.lb.logger/src/server_msg.c Cherrypick from glite-lb_branch_1_1_0_ 2005-08-11 12:03:14 UTC Aleš Křenek 'pushed version to reflect internal testing fixes': org.glite.lb/project/version.properties Delete: org.glite.deployment.lb/config/scripts/remove_all_rpms.sh org.glite.jp.client/.cvsignore org.glite.jp.client/Makefile org.glite.jp.client/build.xml org.glite.jp.client/project/build.number org.glite.jp.client/project/build.properties org.glite.jp.client/project/configure.properties.xml org.glite.jp.client/project/properties.xml org.glite.jp.client/project/tar_exclude org.glite.jp.client/project/version.properties org.glite.jp.client/src/authz.c org.glite.jp.client/src/authz.h org.glite.jp.client/src/backend.h org.glite.jp.client/src/bones_server.c org.glite.jp.client/src/builtin_plugins.h org.glite.jp.client/src/db.h org.glite.jp.client/src/feed.c org.glite.jp.client/src/feed.h org.glite.jp.client/src/file_plugin.c org.glite.jp.client/src/ftp_backend.c org.glite.jp.client/src/is_client.c org.glite.jp.client/src/jpimporter.c org.glite.jp.client/src/jptype_map.h org.glite.jp.client/src/mysql.c org.glite.jp.client/src/new_ftp_backend.c org.glite.jp.client/src/simple_server.c org.glite.jp.client/src/soap_ops.c org.glite.jp.client/src/tags.c org.glite.jp.client/src/tags.h org.glite.jp.client/src/tags_plugin.c org.glite.jp.client/src/typemap.dat org.glite.jp.common/.cvsignore org.glite.jp.common/Makefile org.glite.jp.common/build.xml org.glite.jp.common/interface/context.h org.glite.jp.common/interface/strmd5.h org.glite.jp.common/interface/type_plugin.h org.glite.jp.common/interface/types.h org.glite.jp.common/project/build.number org.glite.jp.common/project/build.properties org.glite.jp.common/project/configure.properties.xml org.glite.jp.common/project/properties.xml org.glite.jp.common/project/tar_exclude org.glite.jp.common/project/version.properties org.glite.jp.common/src/attr.c org.glite.jp.common/src/context.c org.glite.jp.common/src/stdtypes.c org.glite.jp.common/src/strmd5.c org.glite.jp.index/.cvsignore org.glite.jp.index/Makefile org.glite.jp.index/build.xml org.glite.jp.index/project/JobProvenanceIS.wsdl org.glite.jp.index/project/build.number org.glite.jp.index/project/build.properties org.glite.jp.index/project/configure.properties.xml org.glite.jp.index/project/properties.xml org.glite.jp.index/project/tar_exclude org.glite.jp.index/project/version.properties org.glite.jp.index/src/simple_server.c org.glite.jp.index/src/soap_ops.c org.glite.jp.index/src/typemap.dat org.glite.jp.primary/.cvsignore org.glite.jp.primary/Makefile org.glite.jp.primary/build.xml org.glite.jp.primary/config/glite-jp-primary-dbsetup.sql org.glite.jp.primary/examples/README.test org.glite.jp.primary/examples/jpps-test.c org.glite.jp.primary/interface/file_plugin.h org.glite.jp.primary/project/build.number org.glite.jp.primary/project/build.properties org.glite.jp.primary/project/configure.properties.xml org.glite.jp.primary/project/properties.xml org.glite.jp.primary/project/tar_exclude org.glite.jp.primary/project/version.properties org.glite.jp.primary/src/authz.c org.glite.jp.primary/src/authz.h org.glite.jp.primary/src/backend.h org.glite.jp.primary/src/bones_server.c org.glite.jp.primary/src/builtin_plugins.h org.glite.jp.primary/src/db.h org.glite.jp.primary/src/feed.c org.glite.jp.primary/src/feed.h org.glite.jp.primary/src/file_plugin.c org.glite.jp.primary/src/ftp_backend.c org.glite.jp.primary/src/is_client.c org.glite.jp.primary/src/is_client.h org.glite.jp.primary/src/jptype_map.h org.glite.jp.primary/src/mysql.c org.glite.jp.primary/src/new_ftp_backend.c org.glite.jp.primary/src/simple_server.c org.glite.jp.primary/src/soap_ops.c org.glite.jp.primary/src/tags.c org.glite.jp.primary/src/tags.h org.glite.jp.primary/src/tags_plugin.c org.glite.jp.primary/src/typemap.dat org.glite.jp.ws-interface/.cvsignore org.glite.jp.ws-interface/LICENSE org.glite.jp.ws-interface/Makefile org.glite.jp.ws-interface/build.xml org.glite.jp.ws-interface/interface/JobProvenanceIS.wsdl org.glite.jp.ws-interface/project/build.number org.glite.jp.ws-interface/project/build.properties org.glite.jp.ws-interface/project/configure.properties.xml org.glite.jp.ws-interface/project/glite-jp-ws-interface.spec org.glite.jp.ws-interface/project/properties.xml org.glite.jp.ws-interface/project/tar_exclude org.glite.jp.ws-interface/project/version.properties org.glite.jp.ws-interface/src/JobProvenancePS.xml org.glite.jp.ws-interface/src/JobProvenanceTypes.xml org.glite.jp.ws-interface/src/doc.xml org.glite.jp.ws-interface/src/puke-ug.xsl org.glite.jp.ws-interface/src/puke-wsdl.xsl org.glite.jp/.cvsignore org.glite.jp/build.xml org.glite.jp/project/build.number org.glite.jp/project/build.properties org.glite.jp/project/dependencies.properties org.glite.jp/project/glite.jp.csf.xml org.glite.jp/project/properties.xml org.glite.jp/project/run-workspace org.glite.jp/project/taskdefs.xml org.glite.jp/project/version.properties org.glite.security.proxyrenewal/src/acstack.h org.glite.security.proxyrenewal/src/newformat.h org.glite.wms-utils.exception/test/Makefile.am org.glite.wms-utils.exception/test/exception_cu_main.cpp org.glite.wms-utils.exception/test/exception_cu_suite.cpp org.glite.wms-utils.exception/test/exception_cu_suite.h org.glite.wms-utils.jobid/test/Makefile.am org.glite.wms-utils.jobid/test/jobid_cu_main.cpp org.glite.wms-utils.jobid/test/jobid_cu_suite.cpp org.glite.wms-utils.jobid/test/jobid_cu_suite.h org.glite.wms-utils.jobid/test/manipulation_cu_main.cpp org.glite.wms-utils.jobid/test/manipulation_cu_suite.cpp org.glite.wms-utils.jobid/test/manipulation_cu_suite.h org.gridsite.core/doc/admin.html org.gridsite.core/doc/config.html org.gridsite.core/doc/gacl.html org.gridsite.core/doc/install.html org.gridsite.core/doc/library.html org.gridsite.core/doc/module.html org.gridsite.core/doc/user.html --- org.glite.deployment.lb/CHANGELOG | 9 + .../config/scripts/glite-lb-config.py | 199 +- .../config/scripts/remove_all_rpms.sh | 49 - .../config/templates/glite-lb.cfg.xml | 60 +- .../doc/release_notes/release_notes.doc | Bin 267264 -> 165376 bytes .../doc/release_notes/release_notes.html | 3081 +++--- .../doc/release_notes/release_notes.pdf | Bin 217944 -> 40822 bytes org.glite.deployment.lb/project/build.number | 4 +- .../project/glite-lb.sdf.xml.template | 26 +- org.glite.deployment.lb/project/lxscript-rpm.xsl | 12 +- .../project/quattor-template.xsl | 12 + org.glite.deployment.lb/project/version.properties | 2 +- org.glite.jp.client/.cvsignore | 1 - org.glite.jp.client/Makefile | 135 - org.glite.jp.client/build.xml | 86 - org.glite.jp.client/project/build.number | 1 - org.glite.jp.client/project/build.properties | 0 .../project/configure.properties.xml | 43 - org.glite.jp.client/project/properties.xml | 44 - org.glite.jp.client/project/tar_exclude | 10 - org.glite.jp.client/project/version.properties | 2 - org.glite.jp.client/src/authz.c | 76 - org.glite.jp.client/src/authz.h | 18 - org.glite.jp.client/src/backend.h | 116 - org.glite.jp.client/src/bones_server.c | 327 - org.glite.jp.client/src/builtin_plugins.h | 7 - org.glite.jp.client/src/db.h | 83 - org.glite.jp.client/src/feed.c | 327 - org.glite.jp.client/src/feed.h | 21 - org.glite.jp.client/src/file_plugin.c | 115 - org.glite.jp.client/src/ftp_backend.c | 1744 --- org.glite.jp.client/src/is_client.c | 38 - org.glite.jp.client/src/jpimporter.c | 243 - org.glite.jp.client/src/jptype_map.h | 18 - org.glite.jp.client/src/mysql.c | 265 - org.glite.jp.client/src/new_ftp_backend.c | 1790 --- org.glite.jp.client/src/simple_server.c | 59 - org.glite.jp.client/src/soap_ops.c | 465 - org.glite.jp.client/src/tags.c | 233 - org.glite.jp.client/src/tags.h | 1 - org.glite.jp.client/src/tags_plugin.c | 148 - org.glite.jp.client/src/typemap.dat | 3 - org.glite.jp.common/.cvsignore | 1 - org.glite.jp.common/Makefile | 85 - org.glite.jp.common/build.xml | 97 - org.glite.jp.common/interface/context.h | 17 - org.glite.jp.common/interface/strmd5.h | 28 - org.glite.jp.common/interface/type_plugin.h | 49 - org.glite.jp.common/interface/types.h | 67 - org.glite.jp.common/project/build.number | 1 - org.glite.jp.common/project/build.properties | 0 .../project/configure.properties.xml | 52 - org.glite.jp.common/project/properties.xml | 52 - org.glite.jp.common/project/tar_exclude | 10 - org.glite.jp.common/project/version.properties | 2 - org.glite.jp.common/src/attr.c | 13 - org.glite.jp.common/src/context.c | 139 - org.glite.jp.common/src/stdtypes.c | 127 - org.glite.jp.common/src/strmd5.c | 115 - org.glite.jp.index/.cvsignore | 1 - org.glite.jp.index/Makefile | 129 - org.glite.jp.index/build.xml | 98 - org.glite.jp.index/project/JobProvenanceIS.wsdl | 531 - org.glite.jp.index/project/build.number | 1 - org.glite.jp.index/project/build.properties | 0 .../project/configure.properties.xml | 53 - org.glite.jp.index/project/properties.xml | 52 - org.glite.jp.index/project/tar_exclude | 10 - org.glite.jp.index/project/version.properties | 2 - org.glite.jp.index/src/simple_server.c | 39 - org.glite.jp.index/src/soap_ops.c | 81 - org.glite.jp.index/src/typemap.dat | 2 - org.glite.jp.primary/.cvsignore | 1 - org.glite.jp.primary/Makefile | 186 - org.glite.jp.primary/build.xml | 102 - .../config/glite-jp-primary-dbsetup.sql | 46 - org.glite.jp.primary/examples/README.test | 40 - org.glite.jp.primary/examples/jpps-test.c | 245 - org.glite.jp.primary/interface/file_plugin.h | 73 - org.glite.jp.primary/project/build.number | 1 - org.glite.jp.primary/project/build.properties | 0 .../project/configure.properties.xml | 58 - org.glite.jp.primary/project/properties.xml | 51 - org.glite.jp.primary/project/tar_exclude | 10 - org.glite.jp.primary/project/version.properties | 2 - org.glite.jp.primary/src/authz.c | 78 - org.glite.jp.primary/src/authz.h | 18 - org.glite.jp.primary/src/backend.h | 116 - org.glite.jp.primary/src/bones_server.c | 333 - org.glite.jp.primary/src/builtin_plugins.h | 7 - org.glite.jp.primary/src/db.h | 83 - org.glite.jp.primary/src/feed.c | 336 - org.glite.jp.primary/src/feed.h | 21 - org.glite.jp.primary/src/file_plugin.c | 115 - org.glite.jp.primary/src/ftp_backend.c | 1744 --- org.glite.jp.primary/src/is_client.c | 39 - org.glite.jp.primary/src/is_client.h | 1 - org.glite.jp.primary/src/jptype_map.h | 34 - org.glite.jp.primary/src/mysql.c | 265 - org.glite.jp.primary/src/new_ftp_backend.c | 1791 --- org.glite.jp.primary/src/simple_server.c | 59 - org.glite.jp.primary/src/soap_ops.c | 415 - org.glite.jp.primary/src/tags.c | 233 - org.glite.jp.primary/src/tags.h | 1 - org.glite.jp.primary/src/tags_plugin.c | 149 - org.glite.jp.primary/src/typemap.dat | 3 - org.glite.jp.ws-interface/.cvsignore | 2 - org.glite.jp.ws-interface/LICENSE | 69 - org.glite.jp.ws-interface/Makefile | 61 - org.glite.jp.ws-interface/build.xml | 120 - .../interface/JobProvenanceIS.wsdl | 110 - org.glite.jp.ws-interface/project/build.number | 1 - org.glite.jp.ws-interface/project/build.properties | 0 .../project/configure.properties.xml | 54 - .../project/glite-jp-ws-interface.spec | 42 - org.glite.jp.ws-interface/project/properties.xml | 73 - org.glite.jp.ws-interface/project/tar_exclude | 0 .../project/version.properties | 2 - org.glite.jp.ws-interface/src/JobProvenancePS.xml | 112 - .../src/JobProvenanceTypes.xml | 75 - org.glite.jp.ws-interface/src/doc.xml | 2 - org.glite.jp.ws-interface/src/puke-ug.xsl | 156 - org.glite.jp.ws-interface/src/puke-wsdl.xsl | 292 - org.glite.jp/.cvsignore | 1 - org.glite.jp/build.xml | 268 - org.glite.jp/project/build.number | 1 - org.glite.jp/project/build.properties | 0 org.glite.jp/project/dependencies.properties | 12 - org.glite.jp/project/glite.jp.csf.xml | 271 - org.glite.jp/project/properties.xml | 47 - org.glite.jp/project/run-workspace | 10 - org.glite.jp/project/taskdefs.xml | 24 - org.glite.jp/project/version.properties | 2 - org.glite.lb.client/project/version.properties | 2 +- org.glite.lb.client/src/prod_proto.c | 65 +- org.glite.lb.client/src/producer.c | 4 +- org.glite.lb.common/project/version.properties | 2 +- org.glite.lb.common/src/il_msg.c | 21 +- org.glite.lb.common/test/il_msg_test.cpp | 8 +- org.glite.lb.logger/project/version.properties | 2 +- org.glite.lb.server/Makefile | 57 +- .../project/configure.properties.xml | 4 + org.glite.lb.server/project/version.properties | 2 +- org.glite.lb/project/dependencies.properties | 32 +- org.glite.lb/project/version.properties | 2 +- org.glite.security.gsoap-plugin/Makefile | 7 +- org.glite.security.gsoap-plugin/build.xml | 2 +- .../project/build.number | 3 +- .../project/configure.properties.xml | 1 + .../project/version.properties | 3 +- .../src/glite_gsplugin.c | 2 + .../src/stdsoap2_2.6.2.c | 11020 +++++++++++++++++++ org.glite.security.gsoap-plugin/test/test_gss.cpp | 69 +- org.glite.security.proxyrenewal/build.xml | 3 +- .../project/build.number | 3 +- .../project/configure.properties.xml | 11 +- .../project/version.properties | 6 +- org.glite.security.proxyrenewal/src/acstack.h | 79 - org.glite.security.proxyrenewal/src/newformat.h | 195 - org.glite.security.proxyrenewal/src/voms.c | 2 +- org.glite.wms-utils.exception/build.xml | 3 +- org.glite.wms-utils.exception/configure.ac | 8 +- org.glite.wms-utils.exception/project/build.number | 3 +- .../project/version.properties | 6 +- org.glite.wms-utils.exception/src/Exception.cpp | 17 +- org.glite.wms-utils.exception/test/Makefile.am | 33 - .../test/exception_cu_main.cpp | 33 - .../test/exception_cu_suite.cpp | 56 - .../test/exception_cu_suite.h | 29 - org.glite.wms-utils.jobid/build.xml | 3 +- .../interface/glite/wmsutils/jobid/JobId.h | 28 +- org.glite.wms-utils.jobid/project/build.number | 3 +- .../project/version.properties | 6 +- org.glite.wms-utils.jobid/test/Makefile.am | 48 - org.glite.wms-utils.jobid/test/jobid_cu_main.cpp | 33 - org.glite.wms-utils.jobid/test/jobid_cu_suite.cpp | 102 - org.glite.wms-utils.jobid/test/jobid_cu_suite.h | 34 - .../test/manipulation_cu_main.cpp | 33 - .../test/manipulation_cu_suite.cpp | 34 - .../test/manipulation_cu_suite.h | 29 - org.gridsite.core/CHANGES | 57 + org.gridsite.core/INSTALL | 6 +- org.gridsite.core/README | 9 +- org.gridsite.core/VERSION | 2 +- org.gridsite.core/doc/admin.html | 103 - org.gridsite.core/doc/config.html | 196 - org.gridsite.core/doc/delegation-1.wsdl | 91 + org.gridsite.core/doc/findproxyfile.1 | 2 +- org.gridsite.core/doc/gacl.html | 84 - org.gridsite.core/doc/gsexec.8 | 146 +- org.gridsite.core/doc/htcp.1 | 99 +- org.gridsite.core/doc/htfind.1 | 1 + org.gridsite.core/doc/htmv.1 | 1 + org.gridsite.core/doc/htping.1 | 1 + org.gridsite.core/doc/httpd-fileserver.conf | 33 +- org.gridsite.core/doc/httpd-webserver.conf | 30 +- org.gridsite.core/doc/index.html | 76 +- org.gridsite.core/doc/install.html | 158 - org.gridsite.core/doc/library.html | 1 - org.gridsite.core/doc/mod_gridsite.8 | 311 + org.gridsite.core/doc/module.html | 350 - org.gridsite.core/doc/urlencode.1 | 7 +- org.gridsite.core/doc/user.html | 302 - org.gridsite.core/interface/gridsite.h | 43 +- org.gridsite.core/project/version.properties | 2 +- org.gridsite.core/src/Makefile | 103 +- org.gridsite.core/src/gridsite.spec | 14 +- org.gridsite.core/src/grst_asn1.c | 8 +- org.gridsite.core/src/grst_gacl.c | 20 +- org.gridsite.core/src/grst_htcp.c | 311 + org.gridsite.core/src/grst_http.c | 36 + org.gridsite.core/src/grst_x509.c | 131 +- org.gridsite.core/src/gsexec.c | 70 +- org.gridsite.core/src/htcp | Bin 22680 -> 49931 bytes org.gridsite.core/src/htcp.c | 799 +- org.gridsite.core/src/mod_gridsite.c | 1255 ++- org.gridsite.core/src/showx509exts.c | 24 +- 217 files changed, 15872 insertions(+), 21348 deletions(-) delete mode 100755 org.glite.deployment.lb/config/scripts/remove_all_rpms.sh delete mode 100644 org.glite.jp.client/.cvsignore delete mode 100644 org.glite.jp.client/Makefile delete mode 100755 org.glite.jp.client/build.xml delete mode 100644 org.glite.jp.client/project/build.number delete mode 100644 org.glite.jp.client/project/build.properties delete mode 100644 org.glite.jp.client/project/configure.properties.xml delete mode 100755 org.glite.jp.client/project/properties.xml delete mode 100644 org.glite.jp.client/project/tar_exclude delete mode 100644 org.glite.jp.client/project/version.properties delete mode 100644 org.glite.jp.client/src/authz.c delete mode 100644 org.glite.jp.client/src/authz.h delete mode 100644 org.glite.jp.client/src/backend.h delete mode 100644 org.glite.jp.client/src/bones_server.c delete mode 100644 org.glite.jp.client/src/builtin_plugins.h delete mode 100644 org.glite.jp.client/src/db.h delete mode 100644 org.glite.jp.client/src/feed.c delete mode 100644 org.glite.jp.client/src/feed.h delete mode 100644 org.glite.jp.client/src/file_plugin.c delete mode 100644 org.glite.jp.client/src/ftp_backend.c delete mode 100644 org.glite.jp.client/src/is_client.c delete mode 100644 org.glite.jp.client/src/jpimporter.c delete mode 100644 org.glite.jp.client/src/jptype_map.h delete mode 100644 org.glite.jp.client/src/mysql.c delete mode 100644 org.glite.jp.client/src/new_ftp_backend.c delete mode 100644 org.glite.jp.client/src/simple_server.c delete mode 100644 org.glite.jp.client/src/soap_ops.c delete mode 100644 org.glite.jp.client/src/tags.c delete mode 100644 org.glite.jp.client/src/tags.h delete mode 100644 org.glite.jp.client/src/tags_plugin.c delete mode 100644 org.glite.jp.client/src/typemap.dat delete mode 100644 org.glite.jp.common/.cvsignore delete mode 100644 org.glite.jp.common/Makefile delete mode 100755 org.glite.jp.common/build.xml delete mode 100644 org.glite.jp.common/interface/context.h delete mode 100755 org.glite.jp.common/interface/strmd5.h delete mode 100644 org.glite.jp.common/interface/type_plugin.h delete mode 100644 org.glite.jp.common/interface/types.h delete mode 100644 org.glite.jp.common/project/build.number delete mode 100644 org.glite.jp.common/project/build.properties delete mode 100644 org.glite.jp.common/project/configure.properties.xml delete mode 100755 org.glite.jp.common/project/properties.xml delete mode 100644 org.glite.jp.common/project/tar_exclude delete mode 100644 org.glite.jp.common/project/version.properties delete mode 100644 org.glite.jp.common/src/attr.c delete mode 100644 org.glite.jp.common/src/context.c delete mode 100644 org.glite.jp.common/src/stdtypes.c delete mode 100755 org.glite.jp.common/src/strmd5.c delete mode 100644 org.glite.jp.index/.cvsignore delete mode 100644 org.glite.jp.index/Makefile delete mode 100755 org.glite.jp.index/build.xml delete mode 100644 org.glite.jp.index/project/JobProvenanceIS.wsdl delete mode 100644 org.glite.jp.index/project/build.number delete mode 100644 org.glite.jp.index/project/build.properties delete mode 100644 org.glite.jp.index/project/configure.properties.xml delete mode 100755 org.glite.jp.index/project/properties.xml delete mode 100644 org.glite.jp.index/project/tar_exclude delete mode 100644 org.glite.jp.index/project/version.properties delete mode 100644 org.glite.jp.index/src/simple_server.c delete mode 100644 org.glite.jp.index/src/soap_ops.c delete mode 100644 org.glite.jp.index/src/typemap.dat delete mode 100644 org.glite.jp.primary/.cvsignore delete mode 100644 org.glite.jp.primary/Makefile delete mode 100755 org.glite.jp.primary/build.xml delete mode 100644 org.glite.jp.primary/config/glite-jp-primary-dbsetup.sql delete mode 100644 org.glite.jp.primary/examples/README.test delete mode 100644 org.glite.jp.primary/examples/jpps-test.c delete mode 100644 org.glite.jp.primary/interface/file_plugin.h delete mode 100644 org.glite.jp.primary/project/build.number delete mode 100644 org.glite.jp.primary/project/build.properties delete mode 100644 org.glite.jp.primary/project/configure.properties.xml delete mode 100755 org.glite.jp.primary/project/properties.xml delete mode 100644 org.glite.jp.primary/project/tar_exclude delete mode 100644 org.glite.jp.primary/project/version.properties delete mode 100644 org.glite.jp.primary/src/authz.c delete mode 100644 org.glite.jp.primary/src/authz.h delete mode 100644 org.glite.jp.primary/src/backend.h delete mode 100644 org.glite.jp.primary/src/bones_server.c delete mode 100644 org.glite.jp.primary/src/builtin_plugins.h delete mode 100644 org.glite.jp.primary/src/db.h delete mode 100644 org.glite.jp.primary/src/feed.c delete mode 100644 org.glite.jp.primary/src/feed.h delete mode 100644 org.glite.jp.primary/src/file_plugin.c delete mode 100644 org.glite.jp.primary/src/ftp_backend.c delete mode 100644 org.glite.jp.primary/src/is_client.c delete mode 100644 org.glite.jp.primary/src/is_client.h delete mode 100644 org.glite.jp.primary/src/jptype_map.h delete mode 100644 org.glite.jp.primary/src/mysql.c delete mode 100644 org.glite.jp.primary/src/new_ftp_backend.c delete mode 100644 org.glite.jp.primary/src/simple_server.c delete mode 100644 org.glite.jp.primary/src/soap_ops.c delete mode 100644 org.glite.jp.primary/src/tags.c delete mode 100644 org.glite.jp.primary/src/tags.h delete mode 100644 org.glite.jp.primary/src/tags_plugin.c delete mode 100644 org.glite.jp.primary/src/typemap.dat delete mode 100755 org.glite.jp.ws-interface/.cvsignore delete mode 100755 org.glite.jp.ws-interface/LICENSE delete mode 100644 org.glite.jp.ws-interface/Makefile delete mode 100644 org.glite.jp.ws-interface/build.xml delete mode 100644 org.glite.jp.ws-interface/interface/JobProvenanceIS.wsdl delete mode 100644 org.glite.jp.ws-interface/project/build.number delete mode 100755 org.glite.jp.ws-interface/project/build.properties delete mode 100644 org.glite.jp.ws-interface/project/configure.properties.xml delete mode 100644 org.glite.jp.ws-interface/project/glite-jp-ws-interface.spec delete mode 100644 org.glite.jp.ws-interface/project/properties.xml delete mode 100644 org.glite.jp.ws-interface/project/tar_exclude delete mode 100755 org.glite.jp.ws-interface/project/version.properties delete mode 100644 org.glite.jp.ws-interface/src/JobProvenancePS.xml delete mode 100644 org.glite.jp.ws-interface/src/JobProvenanceTypes.xml delete mode 100644 org.glite.jp.ws-interface/src/doc.xml delete mode 100644 org.glite.jp.ws-interface/src/puke-ug.xsl delete mode 100644 org.glite.jp.ws-interface/src/puke-wsdl.xsl delete mode 100644 org.glite.jp/.cvsignore delete mode 100644 org.glite.jp/build.xml delete mode 100644 org.glite.jp/project/build.number delete mode 100644 org.glite.jp/project/build.properties delete mode 100644 org.glite.jp/project/dependencies.properties delete mode 100644 org.glite.jp/project/glite.jp.csf.xml delete mode 100755 org.glite.jp/project/properties.xml delete mode 100644 org.glite.jp/project/run-workspace delete mode 100755 org.glite.jp/project/taskdefs.xml delete mode 100644 org.glite.jp/project/version.properties create mode 100644 org.glite.security.gsoap-plugin/src/stdsoap2_2.6.2.c delete mode 100755 org.glite.security.proxyrenewal/src/acstack.h delete mode 100755 org.glite.security.proxyrenewal/src/newformat.h delete mode 100755 org.glite.wms-utils.exception/test/Makefile.am delete mode 100644 org.glite.wms-utils.exception/test/exception_cu_main.cpp delete mode 100644 org.glite.wms-utils.exception/test/exception_cu_suite.cpp delete mode 100644 org.glite.wms-utils.exception/test/exception_cu_suite.h delete mode 100755 org.glite.wms-utils.jobid/test/Makefile.am delete mode 100644 org.glite.wms-utils.jobid/test/jobid_cu_main.cpp delete mode 100644 org.glite.wms-utils.jobid/test/jobid_cu_suite.cpp delete mode 100644 org.glite.wms-utils.jobid/test/jobid_cu_suite.h delete mode 100644 org.glite.wms-utils.jobid/test/manipulation_cu_main.cpp delete mode 100644 org.glite.wms-utils.jobid/test/manipulation_cu_suite.cpp delete mode 100644 org.glite.wms-utils.jobid/test/manipulation_cu_suite.h delete mode 100644 org.gridsite.core/doc/admin.html delete mode 100644 org.gridsite.core/doc/config.html create mode 100644 org.gridsite.core/doc/delegation-1.wsdl delete mode 100644 org.gridsite.core/doc/gacl.html create mode 100644 org.gridsite.core/doc/htfind.1 create mode 100644 org.gridsite.core/doc/htmv.1 create mode 100644 org.gridsite.core/doc/htping.1 delete mode 100644 org.gridsite.core/doc/install.html delete mode 100644 org.gridsite.core/doc/library.html create mode 100644 org.gridsite.core/doc/mod_gridsite.8 delete mode 100644 org.gridsite.core/doc/module.html delete mode 100644 org.gridsite.core/doc/user.html create mode 100644 org.gridsite.core/src/grst_htcp.c diff --git a/org.glite.deployment.lb/CHANGELOG b/org.glite.deployment.lb/CHANGELOG index 9f1b376..8282547 100644 --- a/org.glite.deployment.lb/CHANGELOG +++ b/org.glite.deployment.lb/CHANGELOG @@ -1,3 +1,12 @@ +DATE: 12-10-2005 00:15 +[dimeglio] Use checkMySQLConfiguration function to check root password + +DATE: 13-09-2005 23:00 +[dimeglio] Use standard mysql module functions to create db + +DATE: 08-07-2005 15:20 +[dimeglio] Increased major version because of interface changes in the config scripts + DATE: 08-07-2005 15:20 [dimeglio] Merged from branch 1.2.2 diff --git a/org.glite.deployment.lb/config/scripts/glite-lb-config.py b/org.glite.deployment.lb/config/scripts/glite-lb-config.py index f63be09..78ce0cf 100644 --- a/org.glite.deployment.lb/config/scripts/glite-lb-config.py +++ b/org.glite.deployment.lb/config/scripts/glite-lb-config.py @@ -6,7 +6,7 @@ # For license conditions see the license file or http://eu-egee.org/license.html # ################################################################################ -# glite-lb-config v. 1.3.0 +# glite-lb-config v. 2.0.4 # # Post-installation script for configuring the gLite Logging and Bookkeping Server # Robert Harakaly < robert.harakaly@cern.ch > @@ -16,9 +16,13 @@ # Version info: $Id$ # # Usage: python glite-lb-config [-c|-v|-h|--help] -# -c print configuration -# -v print version -# -h,--help print usage info +# -c, --checkconf print configuration +# -v, --version print version +# -h,--help print usage info +# --configure configure the service +# --start start the service +# --stop stop the service +# --status show service status # # Return codes: 0 - Ok # 1 - Configuration failed @@ -42,7 +46,7 @@ class glite_lb: def __init__(self): self.mysql = MySQL.Mysql() self.verbose = 0 - self.version = "1.3.0" + self.version = "2.0.4" self.name = "glite-lb" self.friendly_name = "gLite Logging and Bookkeeping" params['module.version'] = self.version @@ -98,6 +102,7 @@ python %s-config [OPTION...]""" % (self.name, os.environ['GLITE_LOCATION'], \ print ' -c, --checkconf print the service configuration' print ' -v, --version print the version of the configuration script' print ' -h, --help print this usage information' + print ' --configure configure the service' print ' --start start the service' print ' --stop stop the service' print ' --status check service status' @@ -178,6 +183,10 @@ python %s-config [OPTION...]""" % (self.name, os.environ['GLITE_LOCATION'], \ print 'The LB Server service has been stopped ', glib.printOkMessage() + #------------------------------------------------------------------- + # MySQL + #------------------------------------------------------------------- + self.mysql.stop() #------------------------------------------------------------------- @@ -215,12 +224,11 @@ python %s-config [OPTION...]""" % (self.name, os.environ['GLITE_LOCATION'], \ #-------------------------------------------------------- if os.system("python %s/glite-security-utils-config.py --subservice" % glib.getScriptPath()): - print "\nInstalling gLite Security Utilities ", + print "\nConfiguring gLite Security Utilities ", glib.printFailedMessage() - return 1 - - print "\nInstalling gLite Security Utilities ", - glib.printOkMessage() + else: + print "\nConfiguring gLite Security Utilities ", + glib.printOkMessage() # Create the GLITE_USER if it doesn't exists print "\nCreating/Verifying the GLITE_USER account %s" % os.environ['GLITE_USER'] @@ -244,17 +252,34 @@ python %s-config [OPTION...]""" % (self.name, os.environ['GLITE_LOCATION'], \ os.chmod("%s/hostkey.pem" % lb_cert_path, 0400) glib.printOkMessage() - # Create the MySQL database - print "\nCreate/Verify the %s database" % params['lb.database.name'] + #-------------------------------------------------------- + # Configure MySQL + #-------------------------------------------------------- + + # start MySQL self.mysql.stop() time.sleep(5) - self.mysql.start() - + self.mysql.start() + if not os.path.exists('/tmp/mysql.sock'): os.symlink('/var/lib/mysql/mysql.sock', '/tmp/mysql.sock') + # ------------------------------------------------------------ + # Check password of MySQL + # ------------------------------------------------------------ + + self.mysql_root_password = params['mysql.root.password'] + if not params.has_key('set.mysql.root.password'): + params['set.mysql.root.password'] = 'false' + setempty = params['set.mysql.root.password'] + if self.mysql.checkMySQLConfiguration(self.mysql_root_password,setempty): + return 1 + + # Create the MySQL database + print "\nCreate/Verify the %s database" % params['lb.database.name'] + # Check if database exists - if self.mysql.existsDB(params['lb.database.name']) != 0: + if self.mysql.existsDB(params['lb.database.name'],self.mysql_root_password) != 0: # Create database print ('\n==> Creating MySQL %s database\n' % params['lb.database.name']) @@ -262,15 +287,14 @@ python %s-config [OPTION...]""" % (self.name, os.environ['GLITE_LOCATION'], \ os.remove('/tmp/mysql_ct') file = open('/tmp/mysql_ct', 'w') - text = ['CREATE DATABASE %s;\n' % params['lb.database.name'], - 'GRANT ALL PRIVILEGES ON %s.* TO %s@localhost IDENTIFIED BY "";\n' \ - % (params['lb.database.name'],params['lb.database.username']), - 'USE %s;\n' % params['lb.database.name'], - '\. %s/etc/glite-lb-dbsetup.sql\n' % os.environ['GLITE_LOCATION']] + + self.mysql.add_user(params['lb.database.name'],params['lb.database.username'],"",self.mysql_root_password) + text = ['USE %s;\n' % params['lb.database.name'], + '\. %s/etc/glite-lb-dbsetup.sql\n' % os.environ['GLITE_LOCATION']] file.writelines(text) file.close() - os.system('/usr/bin/mysql < /tmp/mysql_ct') + os.system('/usr/bin/mysql -p%s < /tmp/mysql_ct' % self.mysql_root_password) os.system('/bin/rm /tmp/mysql_ct') #Starting and stopping the database before the index creation @@ -330,6 +354,13 @@ python %s-config [OPTION...]""" % (self.name, os.environ['GLITE_LOCATION'], \ # Set all environment variables #------------------------------------------------------------------------------- +def loadDefaults(params): + + params['GLITE_LOCATION'] = "/opt/glite" + params['mysql.root.password'] = "" + params['lb.database.name'] = "lbserver20" + params['lb.database.username'] = "lbserver" + def set_env(): # gLite @@ -385,8 +416,9 @@ if __name__ == '__main__': # Load parameters params = {} + loadDefaults(params) try: - opts, args = getopt.getopt(sys.argv[1:], '', ['siteconfig=']) + opts, args = glib.getopt(sys.argv[1:], '', ['siteconfig=']) for o, a in opts: if o == "--siteconfig": params['site.config.url'] = a @@ -415,66 +447,109 @@ if __name__ == '__main__': # Command line opts if any try: - opts, args = getopt.getopt(sys.argv[1:], 'chv', ['checkconf', 'help', 'version','stop','start','status','siteconfig=']) + opts, args = glib.getopt(sys.argv[1:], 'chv', ['checkconf', 'help', 'version','configure','stop','start','status','siteconfig=']) except getopt.GetoptError: service.usage(msg = "Unknown options(s)") sys.exit(1) + if len(opts) == 0: + service.usage() + sys.exit(0) + # Check cli options for o, a in opts: + if o in ("-h", "--help"): service.usage() sys.exit(0) + if o in ("-v", "--version"): service.showVersion() sys.exit(0) + if o in ("-c", "--checkconf"): service.copyright() service.showVersion() glib.print_params(params) sys.exit(0) - if o in ("stop", "--stop"): + + if o == "--configure": + + # Check certificates + if params.has_key('glite.installer.checkcerts'): + if params['glite.installer.checkcerts'] == "true": + if glib.check_certs(params) != 0: + print "An error occurred while configuring the %s service" \ + % service.friendly_name + sys.exit(1) + + # Print configuration parameters + if verbose: + glib.print_params(params) + + service.copyright() + service.showVersion() + service.banner() + + # Stop all services + glib.printInfoMessage("\n\nStopping all running LB services...") service.stop() - sys.exit(0) - if o in ("start", "--start"): - service.start() - sys.exit(0) - if o == "--status": - sys.exit(service.status()) + + # Configure the service + return_result = service.configure() + + if return_result == 0: + + # Stop all services + glib.printInfoMessage("\n\nStopping all running LB services...") + service.stop() + + print "\n\nThe %s configuration was successfully completed\n" % service.friendly_name + print "You can now start the service using the --start option of this script\n\n" + glib.registerService() + + sys.exit(0) + + elif return_result == 2: + + # Stop all services + glib.printInfoMessage("\n\nStopping all running LB services...") + service.stop() + print "\n\nThe %s configuration was completed,\n" % service.friendly_name + print "but warnings were issued. Please revise them and re-run the script\n" + print "or configure LB manually\n" + + sys.exit(2) - # Check certificates - if params.has_key('glite.installer.checkcerts'): - if params['glite.installer.checkcerts'] == "true": - if glib.check_certs(params) != 0: - print "An error occurred while configuring the %s service" \ + else: + print "\n\nAn unrecoverable error occurred while configuring the %s" \ % service.friendly_name - sys.exit(1) - - # Print configuration parameters - if verbose: - glib.print_params(params) - service.copyright() - service.showVersion() - service.banner() - - # Configure the service - if service.configure() == 0: - print "\n%s configuration successfully completed " % service.friendly_name, - glib.printOkMessage() - glib.registerService() - else: - print "\nAn error occurred while configuring the %s " % service.friendly_name, - glib.printFailedMessage() - sys.exit(1) + sys.exit(1) + + if o in ("start", "--start"): + # Start the service + if service.start() == 0: + print "\n\nThe %s was successfully started " % service.friendly_name, + glib.printOkMessage() + sys.exit(0) + else: + print "\n\nAn error occurred while starting the %s " % service.friendly_name, + glib.printFailedMessage() + sys.exit(1) + + if o in ("stop", "--stop"): + # Stop the service + if service.stop() == 0: + print "\n\nThe %s was successfully stopped " % service.friendly_name, + glib.printOkMessage() + sys.exit(0) + else: + print "\n\nAn unrecoverable error occurred while stopping the %s " % service.friendly_name, + glib.printFailedMessage() + sys.exit(1) - # Start the service - if service.start() == 0: - print "\nThe %s was successfully started " % service.friendly_name, - glib.printOkMessage() - else: - print "\nAn error occurred while starting the %s " % service.friendly_name, - glib.printFailedMessage() - sys.exit(1) - + if o == "--status": + sys.exit(service.status()) + diff --git a/org.glite.deployment.lb/config/scripts/remove_all_rpms.sh b/org.glite.deployment.lb/config/scripts/remove_all_rpms.sh deleted file mode 100755 index 2835ee1..0000000 --- a/org.glite.deployment.lb/config/scripts/remove_all_rpms.sh +++ /dev/null @@ -1,49 +0,0 @@ - -#!/bin/sh - -rpm -e edg-fetch-crl-1.0.0-EGEE \ -ca_ArmeSFo-0.23-1 \ -ca_ASGCCA-0.23-1 \ -ca_BEGrid-0.23-1 \ -ca_CERN-0.23-1 \ -ca_CESNET-0.23-1 \ -ca_CNRS-0.23-1 \ -ca_CNRS-DataGrid-0.23-1 \ -ca_CNRS-Projets-0.23-1 \ -ca_CyGrid-0.23-1 \ -ca_DOEGrids-0.23-1 \ -ca_DOESG-Root-0.23-1 \ -ca_ESnet-0.23-1 \ -ca_FNAL-0.23-1 \ -ca_FNAL_KCA-0.23-1 \ -ca_GermanGrid-0.23-1 \ -ca_Grid-Ireland-0.23-1 \ -ca_GridCanada-0.23-1 \ -ca_HellasGrid-0.23-1 \ -ca_INFN-0.23-1 \ -ca_IUCC-0.23-1 \ -ca_LIP-0.23-1 \ -ca_NIKHEF-0.23-1 \ -ca_NorduGrid-0.23-1 \ -ca_PK-Grid-0.23-1 \ -ca_PolishGrid-0.23-1 \ -ca_Russia-0.23-1 \ -ca_SlovakGrid-0.23-1 \ -ca_Spain-0.23-1 \ -ca_UKeScience-0.23-1 \ -glite-wms-utils-exception-0.1.0-0 \ -glite-wms-utils-jobid-0.1.0-0 \ -glite-lb-client-interface-0.2.0-0 \ -glite-lb-server-bones-0.0.0-0 \ -glite-lb-common-0.2.0-0 \ -glite-lb-logger-0.2.0-0 \ -glite-lb-server-0.3.0-0 \ -glite-security-proxyrenewal-0.1.0-1 \ -ares-1.1.1-EGEE \ -gpt-VDT1.2.0rh9-1 \ -vdt_globus_essentials-VDT1.2.0rh9-1 \ -perl-Expect.pm-1.01-9 \ -myproxy-1.14-EGEE \ -MySQL-client-4.0.20-0 \ -MySQL-server-4.0.20-0 - diff --git a/org.glite.deployment.lb/config/templates/glite-lb.cfg.xml b/org.glite.deployment.lb/config/templates/glite-lb.cfg.xml index ed39d97..97995a8 100644 --- a/org.glite.deployment.lb/config/templates/glite-lb.cfg.xml +++ b/org.glite.deployment.lb/config/templates/glite-lb.cfg.xml @@ -17,6 +17,10 @@ parameter. Leave it empty of comment it out to use the same as 'glite.user.name'" value="changeme"/> + + @@ -30,12 +34,18 @@ description="Enable check of host certificates" value="true"/> - - + + + + + + @@ -45,15 +55,6 @@ - - - - - - @@ -66,28 +67,23 @@ value="true"/> + + + description="Name of the service. This should be globally unique. + [Example: 'HOSTANME_LB_LocalLogger'] [Type: 'String']" + value="${HOSTNAME}_${rgma.servicetool.service_type}"/> - - + value="not available"/> + value="30"/> + value="not available"/> + value="not available"/> diff --git a/org.glite.deployment.lb/doc/release_notes/release_notes.doc b/org.glite.deployment.lb/doc/release_notes/release_notes.doc index ae9fd1814da46c1ba144a0202e5185f6fe1069cf..a1d8d86964cc1c9faae5bdbfc91afb2b7ecc8b04 100644 GIT binary patch literal 165376 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 OdELpk_3n=&f&T;6Ql`lO literal 267264 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 - - - - - - -
- -

gLite Logging & Bookkeeping Server

- -

1. Release Description

- -

This release contains the gLite Logging & Bookkeeping -Server module v. 1.2.3. The following sections provide additional information about -the release content, the module dependencies, the know bugs and issues and a -list of bugs closed since the previous release. For information about -installing and using the gLite Logging & Bookkeeping Server, please refer -to the gLite Installation and User Guides.

- -

2. Changes in this Release

- -

This release introduces the following changes:

- -

 

- -
    -
  • All R-GMA service publishing - instances in the configuration template now have default values
  • -
  • Bug fixes (see below for the complete lists)
  • -
- -

3. Release contents

- -

The gLite Logging & Bookkeeping Server v. 1.2.3 is -composed of the following gLite components:

- -

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Component name

-
-

Version

-
-

File

-
-

org.glite.deployment.lb

-
-

1.2.3

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/installers/glite-lb_installer.sh -

-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/noarch/RPMS/glite-lb-config-1.2.3-1.noarch.rpm

-

apt-get install - glite-lb-config

-
-

org.glite.deployment.config

-
-

1.1.1

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/noarch/RPMS/glite-config-1.1.1-3.noarch.rpm

-
-

org.glite.lb.client-interface

-
-

1.0.3

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-lb-client-interface-1.0.3-1.i386.rpm

-
-

org.glite.lb.common

-
-

1.1.4

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-lb-common-1.1.4-1.i386.rpm

-
-

org.glite.lb.logger

-
-

1.0.1

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-lb-logger-1.0.1-1.i386.rpm

-
-

org.glite.lb.server

-
-

1.0.1

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-lb-server-1.0.1-1.i386.rpm

-
-

org.glite.lb.server-bones

-
-

1.0.0

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-lb-server-bones-1.0.0-1.i386.rpm

-
-

org.glite.lb.ws-interface

-
-

1.0.1

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-lb-ws-interface-1.0.1-1.i386.rpm

-
-

org.glite.security.proxyrenewal

-
-

1.0.13

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-security-proxyrenewal-1.0.13-1.i386.rpm

-
-

org.glite.wms-utils.exception

-
-

1.0.1

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-wms-utils-exception-1.0.1-1.i386.rpm

-
-

org.glite.wms-utils.jobid

-
-

1.0.0

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-wms-utils-jobid-1.0.0-1.i386.rpm

-
-

org.glite.security.voms

-
-

1.2.32

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/glite-security-voms-1.2.32-1.i386.rpm

-
-

org.gridsite.core

-
-

1.1.5

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/bin/rhel30/i386/RPMS/gridsite-1.1.5-1.i386.rpm

-
- -

 

- -

4. Dependencies

- -

The gLite Logging & Bookkeeping Server v. 1.2.2 module -has the following dependencies:

- -

 

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-

Component name

-
-

Version

-
-

RPM file name

-
-

gLite Security Utilities

-
-

1.0.1

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/installers/glite-security-utils_installer.sh

-
-

gLite R-GMA Service Publisher

-
-

4.2.0

-
-

http://glite.web.cern.ch/glite/packages/R1.1/R20050430/installers/glite-rgma-servicetool_installer.sh

-
-

GPT

-
-

VDT 1.2.2

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/gpt-VDT1.2.2rh9-1.i386.rpm

-
-

VDT Globus Essentials

-
-

VDT 1.2.2

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/vdt_globus_essentials-VDT1.2.2rh9-1.i386.rpm

-
-

MySQL-server

-
-

4.0.20

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/MySQL-server-4.0.20-0.i386.rpm

-
-

MySQL-client

-
-

4.0.20

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/MySQL-client-4.0.20-0.i386.rpm

-
-

ares

-
-

1.1.1

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/ares-1.1.1-EGEE.i386.rpm

-
-

myproxy

-
-

1.14

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/myproxy-1.14-EGEE.i386.rpm

-
-

perl-Expect.pm

-
-

1.01

-
-

http://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/per-Expect.pm-1.01-9.i386.rpm

-
-

Java SDK/JRE

-
-

1.4.2

-
-

http://java.sun.com/j2se/1.4.2/download.html

-
- -

 

- -

5. Known bugs and issues

- -

This release has the -following bugs and issues. Bug numbers refer to the gLite Bug Tracking system -database hosted on the CERN Savannah system at https://savannah.cern.ch/bugs/?group=jra1mdw -.

- -

 

+ margin-left:54.0pt; + text-align:justify; + text-indent:-18.0pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListNumber4, li.MsoListNumber4, div.MsoListNumber4 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:72.0pt; + text-align:justify; + text-indent:-18.0pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListNumber5, li.MsoListNumber5, div.MsoListNumber5 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:90.0pt; + text-align:justify; + text-indent:-18.0pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoTitle, li.MsoTitle, div.MsoTitle + {margin-top:12.0pt; + margin-right:0mm; + margin-bottom:3.0pt; + margin-left:0mm; + text-align:center; + font-size:16.0pt; + font-family:Arial; + font-weight:bold;} +p.MsoClosing, li.MsoClosing, div.MsoClosing + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:216.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoSignature, li.MsoSignature, div.MsoSignature + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:216.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyText, li.MsoBodyText, div.MsoBodyText + {margin-top:3.0pt; + margin-right:0mm; + margin-bottom:3.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyTextIndent, li.MsoBodyTextIndent, div.MsoBodyTextIndent + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:18.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListContinue, li.MsoListContinue, div.MsoListContinue + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:18.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListContinue2, li.MsoListContinue2, div.MsoListContinue2 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:36.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListContinue3, li.MsoListContinue3, div.MsoListContinue3 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:54.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListContinue4, li.MsoListContinue4, div.MsoListContinue4 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:72.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoListContinue5, li.MsoListContinue5, div.MsoListContinue5 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:90.0pt; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoMessageHeader, li.MsoMessageHeader, div.MsoMessageHeader + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:54.0pt; + text-align:justify; + text-indent:-54.0pt; + background:#CCCCCC; + border:none; + padding:0mm; + font-size:12.0pt; + font-family:Arial;} +p.MsoSubtitle, li.MsoSubtitle, div.MsoSubtitle + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:3.0pt; + margin-left:0mm; + text-align:center; + font-size:12.0pt; + font-family:Arial;} +p.MsoSalutation, li.MsoSalutation, div.MsoSalutation + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoDate, li.MsoDate, div.MsoDate + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyTextFirstIndent, li.MsoBodyTextFirstIndent, div.MsoBodyTextFirstIndent + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:0mm; + text-align:justify; + text-indent:10.5pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyTextFirstIndent2, li.MsoBodyTextFirstIndent2, div.MsoBodyTextFirstIndent2 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:18.0pt; + text-align:justify; + text-indent:10.5pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoNoteHeading, li.MsoNoteHeading, div.MsoNoteHeading + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyText2, li.MsoBodyText2, div.MsoBodyText2 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:0mm; + text-align:justify; + line-height:200%; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyText3, li.MsoBodyText3, div.MsoBodyText3 + {margin-top:2.5pt; + margin-right:0mm; + margin-bottom:2.5pt; + margin-left:0mm; + text-align:justify; + page-break-after:avoid; + font-size:10.0pt; + font-family:Arial;} +p.MsoBodyTextIndent2, li.MsoBodyTextIndent2, div.MsoBodyTextIndent2 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:18.0pt; + text-align:justify; + line-height:200%; + font-size:11.0pt; + font-family:"Times New Roman";} +p.MsoBodyTextIndent3, li.MsoBodyTextIndent3, div.MsoBodyTextIndent3 + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:18.0pt; + text-align:justify; + font-size:8.0pt; + font-family:"Times New Roman";} +p.MsoBlockText, li.MsoBlockText, div.MsoBlockText + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + text-indent:17.0pt; + font-size:11.0pt; + font-family:"Times New Roman";} +a:link, span.MsoHyperlink + {color:blue; + text-decoration:underline;} +a:visited, span.MsoHyperlinkFollowed + {color:purple; + text-decoration:underline;} +p.MsoDocumentMap, li.MsoDocumentMap, div.MsoDocumentMap + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + background:navy; + font-size:11.0pt; + font-family:Tahoma;} +p.MsoPlainText, li.MsoPlainText, div.MsoPlainText + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:10.0pt; + font-family:"Courier New";} +p.MsoAutoSig, li.MsoAutoSig, div.MsoAutoSig + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman";} +p + {margin-top:5.0pt; + margin-right:0mm; + margin-bottom:5.0pt; + margin-left:0mm; + text-align:justify; + font-size:12.0pt; + font-family:"Times New Roman";} +address + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:"Times New Roman"; + font-style:italic;} +pre + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:10.0pt; + font-family:"Courier New";} +tt + {font-family:"Courier New";} +p.MsoCommentSubject, li.MsoCommentSubject, div.MsoCommentSubject + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:10.0pt; + font-family:"Times New Roman"; + font-weight:bold;} +p.MsoAcetate, li.MsoAcetate, div.MsoAcetate + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:8.0pt; + font-family:Tahoma;} +p.3eretraitnormal, li.3eretraitnormal, div.3eretraitnormal + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:3.0pt; + margin-left:102.9pt; + text-align:justify; + text-indent:-17.85pt; + font-size:12.0pt; + font-family:"Times New Roman";} +p.2eretraitjustifi, li.2eretraitjustifi, div.2eretraitjustifi + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:3.0pt; + margin-left:40.0mm; + text-align:justify; + text-indent:-7.1pt; + line-height:12.0pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.2eretraitnormal, li.2eretraitnormal, div.2eretraitnormal + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:3.0pt; + margin-left:18.0pt; + text-align:justify; + text-indent:-18.0pt; + font-size:12.0pt; + font-family:"Times New Roman";} +p.1erretraitnormal, li.1erretraitnormal, div.1erretraitnormal + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:12.0pt; + margin-left:0mm; + text-align:justify; + font-size:12.0pt; + font-family:"Times New Roman";} +p.titrebloc, li.titrebloc, div.titrebloc + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:Arial; + font-weight:bold;} +p.TitreTable, li.TitreTable, div.TitreTable + {margin-top:6.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:center; + font-size:12.0pt; + font-family:Arial; + font-weight:bold;} +p.form, li.form, div.form + {margin-top:6.0pt; + margin-right:7.05pt; + margin-bottom:0mm; + margin-left:0mm; + margin-bottom:.0001pt; + text-align:justify; + background:black; + border:none; + padding:0mm; + font-size:14.0pt; + font-family:"Univers \(W1\)"; + color:white; + text-transform:uppercase; + font-weight:bold;} +p.HB, li.HB, div.HB + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:12.0pt; + margin-left:0mm; + text-align:justify; + page-break-after:avoid; + font-size:12.0pt; + font-family:"Times New Roman"; + color:black; + font-weight:bold;} +p.reference, li.reference, div.reference + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:justify; + page-break-after:avoid; + font-size:9.0pt; + font-family:Arial;} +p.1erretraitjustifi, li.1erretraitjustifi, div.1erretraitjustifi + {margin-top:6.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:14.2pt; + text-align:justify; + text-indent:-14.2pt; + font-size:11.0pt; + font-family:"Times New Roman";} +p.ZonetatEnTte, li.ZonetatEnTte, div.ZonetatEnTte + {margin-top:2.0pt; + margin-right:2.85pt; + margin-bottom:2.0pt; + margin-left:2.85pt; + text-align:center; + page-break-after:avoid; + font-size:36.0pt; + font-family:Arial; + text-transform:uppercase; + font-weight:bold;} +p.DocTitle, li.DocTitle, div.DocTitle + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:center; + font-size:22.0pt; + font-family:Arial; + font-variant:small-caps; + color:gray; + letter-spacing:4.0pt; + font-weight:bold;} +p.DocDate, li.DocDate, div.DocDate + {margin-top:6.0pt; + margin-right:0mm; + margin-bottom:6.0pt; + margin-left:0mm; + text-align:justify; + font-size:11.0pt; + font-family:Arial; + layout-grid-mode:line; + font-weight:bold;} +p.DocSubTitle, li.DocSubTitle, div.DocSubTitle + {margin-top:2.0pt; + margin-right:0mm; + margin-bottom:2.0pt; + margin-left:0mm; + text-align:center; + line-height:12.0pt; + font-size:12.0pt; + font-family:Arial; + font-variant:small-caps; + color:gray; + letter-spacing:4.0pt; + font-weight:bold;} + /* Page Definitions */ + @page Section1 + {size:595.3pt 841.9pt; + margin:70.85pt 70.85pt 70.85pt 70.85pt;} +div.Section1 + {page:Section1;} + /* List Definitions */ + ol + {margin-bottom:0mm;} +ul + {margin-bottom:0mm;} +--> + -
    -
  • If the mysql root password is set - and it is not specified in the mysql.conf file, the configuration script - fails. This bug will be fixed in the next release
  • -
  • No removal procedure is provided - with this release apart from the removal of the RPMS. Any account, group - or other resource created during the module configuration must be manually - cleaned.
  • -
+ -

 

+ -

Known open bugs:

+
-

 

+

gLite Logging & Bookkeeping Server

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +

1. Release Description

+ +

This release contains the gLite Logging & Bookkeeping +Server module v. 2.0.2. The following sections provide additional information about +the release content, the module dependencies, the know bugs and issues and a +list of bugs closed since the previous release. For information about +installing and using the gLite Logging & Bookkeeping Server, please refer +to the gLite Installation and User Guides.

+ +

2. Changes in this release

+ +

2.1. Changes in functionality

+ +

The mysql database can now be protected +with a root password. The mysql root password can be set in the configuration +file.

+ +

2.2. Changes in Configuration

+ +

The following new parameters have been +added to the glite-security-utils.cfg.xml file:

+ +

 

+ +
-

Bug number

-
-

Description

-
-

 

-
-

 #7053

-
-

LB configuration fails if the - mysql root pwd is set 

-
-

 

-
-

 #7237

-
-

Intermittent errors with job - submission 

-
-

 

-
-

 #7300

-
-

update of the lb instructions - at the end of the installer script  

-
-

 

-
-

 #7305

-
-

lb.database.username paramenter - in config file 

-
-

 

-
-

 #7307

-
-

lb config script does _not_ - fail if mysql root password is set 

-
-

 

-
- - - - - -
-

 #7324

+

Parameter name

-

lb-bkserver is running with no - pid 

+
+

Default value

-

 

+
+

Description

-

 #7389

+
+

mysql.root.password

-

LB server and WMS local logger - related issues 

+

<changeme>

-

 

+

The mysql root password

-

 

+

 

-

Bugs fixed in this or previous releases, but not yet officially -tested:

+

The following parameters have been changed +in the glite-security-utils.cfg.xml file:

-

 

+

 

- - - - - - - - - - - - - - - - - - - - - - - +
-

Bug number

+

Parameter name

-

Description

+

Old value

-

 

-
-

 #6412

-
-

--start and --stop options not - documented in glite-ce-config.py, glite-lb-config.py 

-
-

 

-
-

 #6722

-
-

glite-job-status -all doesn't - work 

-
-

 

-
-

 #7151

-
-

There are conflicts when - installing WMS and LB on the same node 

-
-

 

-
-

 #7180

-
-

Logging & Bookkeping UI  

+

New value

-

 

+
+

Description

+ +

 

+ +

The following new parameters have been +removed from the glite-security-utils.cfg.xml file:

+ +

 

+ + - - - - - - - - -
-

 #7321

-
-

creation of indices fails - randomly 

+

Parameter name

-

 

+
+

Comment

-

 #7884

-
-

local header files distributed - in RPMs.  

+
+

lb.database.name

-

 

+

The LB database name cannot be configured + internally, therefore this parameter was ignored

-

 #7910

-
-

Duplicate apostroph in MySQL - calls 

+
+

lb.database.username

-

 

+

The LB database user cannot be configured + internally, therefore this parameter was ignored

-

 

+

 

-

6. Bugs closed since last release

+

3. Release contents

-

This release fixes the -following bugs and issues. Bug numbers refer to the gLite Bug Tracking system -database hosted on the CERN Savannah system at https://savannah.cern.ch/bugs/?group=jra1mdw

+

3.1. Glite sub-deployment modules

-

 

+

The gLite Logging and Bookkeeping module requires the +following sub-modules:

- +

 

+ +

-          +gLite Security Utilities

+ +

-          +gLite R-GMA Servicetool

+ +

 

+ +

The sub-modules are automatically installed with the LB +module. For more information about these sub-modules please refer to the +specific release notes and installation instructions.

+ +

3.2. Glite RPMS

+ +

The gLite Logging & Bookkeeping Server v. 2.0.2 is +composed of the following gLite components:

+ +

 

+ +
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + - + - + - + - + - + - + -
-

Bug number

+

Component name

-

Description

+

Description

-

 #5850

+
+

Version

-

glite-lb-config.py has - glite.location and globus.location not set in params[] 

+
+

File

-

 #5901

+

glite-config

-

mysqlaccess command fails with - Broken pipe if mysql socket file is in /tmp  

-
-

 #5908

-
-

Environment variables set via - the configuration script are not passed to daemon startup scripts 

-
-

 #6057

-
-

glite-lb configuration scripts - has missing dependency (CGI.pm) 

+

gLite configuration scripts

-

 #6075

-
-

glite-lb-config.py crashes with - KeyError: GLITE_CERT_DIR 

+

1.4.5

-

 #6190

-
-

LB local logger doesn't start - on the CE node 

+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-config-1.4.2-1.noarch.rpm

-

 #6366

-
-

LB install script:: Fails but - no error reported 

+
+

glite-lb-client-interface

-

 #6415

+

L&B client library + header files

-

glite-lb-bkserver does not - start and blocks execution of glite-lb-config.py 

+
+

2.0.0

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-lb-client-interface-2.0.0-1.i386.rpm

+

glite-lb-common

+
-

 #7296

+

L&B common subroutines + library

-

glite-lb-config.py crashes with - a TypeError exception 

+
+

2.0.1

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-lb-common-2.0.1-1.i386.rpm

+

glite-lb-config

+
-

 #7753

+

gLite Logging and + Bookkeeping node configuration files

-

glite-lb-config.py fails with - an indentation error 

+
+

2.0.1

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-lb-config-2.0.1-1.noarch.rpm

+

glite-lb-logger

+
-

 #7976

+

L&B local logger

-

edg-job-status not working with - voms proxies 

+
+

1.1.2

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-lb-logger-1.1.2-1.i386.rpm

+

glite-lb-server

+
-

 #8094

+

L&B bookkeeping server

-

interlogd on the WMS doesn't - restart 

+
+

1.2.5

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-lb-server-1.2.5-1.i386.rpm

- -

 

- -
-
- -

7. Previous Releases

- -

7.1. Release 1.2.2

- -

7.1.1. Release Description

- -

This release contains the gLite Logging & Bookkeeping -Server module v. 1.2.2. The following sections provide additional information -about the release content, the module dependencies, the know bugs and issues -and a list of bugs closed since the previous release. For information about -installing and using the gLite Logging & Bookkeeping Server, please refer -to the gLite Installation and User Guides.

- -

7.1.2. Changes in this Release

- -

This release introduces the following changes:

- -

 

- -
    -
  • Implemented status method
  • -
  • Added definition of PERL5LIB env var
  • -
  • Stopping and starting the database before the index - creation (just after the database is created and the user granted) to fix - access denied error
  • -
  • Moved creation of indices inside database creation (if - database exists indices must not be recreated)
  • -
  • GLITE_USER parameter is not exposed anymore in the - configuration file; instead the module uses the same user parameters as - WMS to allow installation on same node
  • -
  • LB admin tools are now installed in sbin, not in bin
  • -
  • Bug fixes (see below for the complete lists)
  • -
- -

7.1.3. Release contents

- -

The gLite Logging & Bookkeeping Server v. 1.2.2 is -composed of the following gLite components:

- -

 

- - - + - - - - - + - - - + - - - + - - - + - - - + - + - - - - - + - - - + - - - + - - - + - - - + - - - + - - - +
+

glite-lb-server-bones

+
-

Component name

+

L&B server bones

-

Version

+
+

2.0.0

-

File

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-lb-server-bones-2.0.0-1.i386.rpm

-

org.glite.deployment.lb

+

glite-lb-ws-interface

-

1.2.2

+

 

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/installers/glite-lb_installer.sh -

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/noarch/RPMS/glite-lb-config-1.2.2-1.noarch.rpm

+

2.0.0

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-lb-ws-interface-2.0.0-1.i386.rpm

-

org.glite.deployment.config

+

glite-rgma-api-java

-

1.0.0

+

Java API for R-GMA

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/noarch/RPMS/glite-config-1.0.0-1.noarch.rpm

+

4.1.5

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-rgma-api-java-4.1.5-1.noarch.rpm

-

org.glite.lb.client-interface

+

glite-rgma-base

-

1.0.2

+

R-GMA basic configuration + and documentation

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-lb-client-interface-1.0.2-1.i386.rpm

+

4.1.19

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-rgma-base-4.1.19-1.noarch.rpm

-

org.glite.lb.common

+

glite-rgma-common-config

-

1.1.4

+

gLite rgma common + configuration items installation

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-lb-common-1.1.4-1.i386.rpm

+

5.0.0

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-rgma-common-config-5.0.0-1.noarch.rpm

-

org.glite.lb.logger

+

glite-rgma-servicetool

-

1.0.1

+

R-GMA service tool

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-lb-logger-1.0.1-1.i386.rpm

+

4.1.19

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-rgma-servicetool-4.1.19-3.noarch.rpm

-

org.glite.lb.server

+

glite-rgma-servicetool-config

+
+

gLite R-GMA servicetool + installation

-

1.0.1

+

5.1.0

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-lb-server-1.0.1-1.i386.rpm

+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-rgma-servicetool-config-5.1.0-1.noarch.rpm

-

org.glite.lb.server-bones

+

glite-rgma-stubs-servlet-java

-

1.0.0

+

Java client implementation + stubs for R-GMA

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-lb-server-bones-1.0.0-1.i386.rpm

+

4.1.13

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-rgma-stubs-servlet-java-4.1.13-1.noarch.rpm

-

org.glite.lb.ws-interface

+

glite-security-gsoap-plugin

-

1.0.1

+

gSOAP plugin and gss + libraries

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-lb-ws-interface-1.0.1-1.i386.rpm

+

1.1.1

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-security-gsoap-plugin-1.1.1-0.i386.rpm

-

org.glite.security.proxyrenewal

+

glite-security-trustmanager

-

1.0.11

+

The java certificate path + checkin for proxy certs in SSL with plugins for tomcat and axis.

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-security-proxyrenewal-1.0.11-1.i386.rpm

+

1.7.3

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-security-trustmanager-1.7.3-1.noarch.rpm

-

org.glite.wms-utils.exception

+

glite-security-util-java

-

1.0.1

+

The java utilities library + for security

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-wms-utils-exception-1.0.1-1.i386.rpm

+

1.1.2

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-security-util-java-1.1.2-2.noarch.rpm

-

org.glite.wms-utils.jobid

+

glite-security-utils-config

-

1.0.0

+

gLite Security Utilities + configuration files

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-wms-utils-jobid-1.0.0-1.i386.rpm

+

1.0.4

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/noarch/RPMS/glite-security-utils-config-1.0.4-1.noarch.rpm

-

org.glite.security.voms

+

glite-security-voms-api-c

-

1.2.32

+

 

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/glite-security-voms-1.2.32-1.i386.rpm

+

1.6.3

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-security-voms-api-c-1.6.3-0.i386.rpm

-

org.gridsite.core

+

glite-wms-utils-exception

-

1.1.5

+

 

-

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/bin/rhel30/i386/RPMS/gridsite-1.1.5-1.i386.rpm

+

1.0.2

+
+

http://glite.web.cern.ch/glite/packages/R1.4/R20050916/bin/rhel30/i386/RPMS/glite-wms-utils-exception-1.0.2-1.i386.rpm

-

 

+

 

-

7.1.4. Dependencies

+

4. Dependencies

-

The gLite Logging & Bookkeeping Server v. 1.2.2 module +

The gLite Logging & Bookkeeping Server v. 2.0.2 module has the following dependencies:

 

@@ -1913,45 +1653,6 @@ has the following dependencies:

-

gLite Security Utilities

- - -

1.0.0

- - -

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/installers/glite-security-utils_installer.sh

- - - - -

gLite R-GMA Service Publisher

- - -

4.1.5

- - -

http://glite.web.cern.ch/glite/packages/R1.0/R20050331/installers/glite-rgma-servicetool_installer.sh

- - - -

GPT

@@ -1963,10 +1664,14 @@ has the following dependencies:

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/gpt-VDT1.2.2rh9-1.i386.rpm

+ lang=EN-GB>VDT1.2.2rh9-1.i386.rpmH

@@ -1983,10 +1688,14 @@ has the following dependencies:

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/vdt_globus_essentials-VDT1.2.2rh9-1.i386.rpm

+ lang=EN-GB>VDT1.2.2rh9-1.i386.rpmH

@@ -1998,15 +1707,19 @@ has the following dependencies:

-

4.0.20

+

4.1.11

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/MySQL-server-4.0.20-0.i386.rpm

+ lang=EN-GB>4.0.20-0.i386.rpmH

@@ -2018,15 +1731,19 @@ has the following dependencies:

-

4.0.20

+

4.1.11

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/MySQL-client-4.0.20-0.i386.rpm

+ lang=EN-GB>4.0.20-0.i386.rpmH

@@ -2043,10 +1760,14 @@ has the following dependencies:

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/ares-1.1.1-EGEE.i386.rpm

+ lang=EN-GB>1.1.1-EGEE.i386.rpmH

@@ -2063,10 +1784,14 @@ has the following dependencies:

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/myproxy-1.14-EGEE.i386.rpm

+ lang=EN-GB>1.14-EGEE.i386.rpmH

@@ -2083,10 +1808,14 @@ has the following dependencies:

-

Hhttp://glite.web.cern.ch/glite/packages/externals/bin/rhel30/RPMS/per-Expect.pm-1.01-9.i386.rpm

+ lang=EN-GB>1.01-9.i386.rpmH

@@ -2111,13 +1840,14 @@ has the following dependencies:

 

-

7.1.5. Known bugs and issues

+

5. Known bugs and issues

-

This release has the -following bugs and issues. Bug numbers refer to the gLite Bug Tracking system -database hosted on the CERN Savannah system at https://savannah.cern.ch/bugs/?group=jra1mdw -.

+

This +release has the following bugs and issues. Bug numbers refer to the gLite Bug +Tracking system database hosted on the CERN Savannah system at Hhttps://savannah.cern.ch/bugs/?group=jra1mdwH .

 

@@ -2137,409 +1867,369 @@ href="https://savannah.cern.ch/bugs/?group=jra1mdw">https://savannah.cern.ch/bug - - - - - - - - - - - - - - - - - - - - - - - + - - - - -
-

Bug number

-
-

Description

-
-

 

-
-

 #6412

-
-

--start and --stop options not - documented in glite-ce-config.py, glite-lb-config.py 

-
-

 

-
-

 #7053

-
-

LB configuration fails if the - mysql root pwd is set 

-
-

 

-
-

 #7180

-
-

Logging & Bookkeping UI  

-
-

 

-
-

 #7237

-
-

Intermittent errors with job - submission 

+

Bug number

-

 

+
+

Description

+
+

 

-

 #7300

+

 #7324

-

update of the lb instructions - at the end of the installer script  

+

lb-bkserver is + running with no pid 

-

 

+

 

-

 #7305

+

 #9148

-

lb.database.username - paramenter in config file 

+

Job stays + 'Submitted' forever 

-

 

+

 

-

 #7307

+

 #9701

-

lb config script does _not_ - fail if mysql root password is set 

+

erroneous rpath + in several shared objects 

-

 

+

 

-

 #7321

+

 #9777

-

creation of indices fails - randomly 

+

unable to get + logging-info -2 info for 1000 jobs 

-

 

+

 

-

 #7324

+

 #10781

-

lb-bkserver is running with no - pid 

+

Missing the + timestamps of 'Scheduled' and 'Running' status 

-

 

-
-

 #7389

-
-

LB server and WMS local logger - related issues 

-
-

 

+

 

 

-

Bugs fixed in this or previous releases, but not yet -officially tested:

+

Bugs fixed in this or previous releases, but not yet officially +tested:

 

- - - - - - - - - - + + + + +
-

Bug number

+

Bug number

-

Description

+

Description

-

 

+

 

-

 #5850

+

 #7053

-

glite-lb-config.py has - glite.location and globus.location not set in params[] 

+

LB configuration fails if the + mysql root pwd is set 

-

 

+

 

-

 #5908

+

 #7300

-

Environment variables set via - the configuration script are not passed to daemon startup scripts 

+

update of the lb instructions at + the end of the installer script  

-

 

+

 

-

 #6075

+

 #7305

-

glite-lb-config.py crashes with - KeyError: GLITE_CERT_DIR 

+

lb.database.username paramenter in + config file 

-

 

+

 

-

 #6366

+

 #7307

-

LB install script:: Fails but - no error reported 

+

lb config script does _not_ fail + if mysql root password is set 

-

 

+

 

-

 #6415

+

#7237

-

glite-lb-bkserver does not - start and blocks execution of glite-lb-config.py 

+

Intermittent errors with job + submission 

-

 

+

 

-

 #6689

+

 #7910

-

glite-proxy-renewd starts the - daemon glite-proxy-renewd as GLITE_USER which is glite-lb i.e. wrong 

+

Duplicate apostroph in MySQL calls 

-

 

+

 

-

 #6722

+

 #8630

-

glite-job-status -all doesn't - work 

+

EDG_WL_* variables in LB library 

-

 

+

 

-

 #7296

+

 #9135

-

glite-lb-config.py crashes with - a TypeError exception 

+

The interlogd has problem sending + event to the LB server 

-

 

+

 

+
+

 #9183

+
+

Broken connections in LB contect + connection pool 

+
+

 

@@ -2548,183 +2238,162 @@ officially tested:

text-align:left;text-autospace:none'> 

-

7.1.6. Bugs closed since last release

+

6. Bugs closed since last release

-

This release fixes the -following bugs and issues. Bug numbers refer to the gLite Bug Tracking system -database hosted on the CERN Savannah system at https://savannah.cern.ch/bugs/?group=jra1mdw

+

This +release fixes the following bugs and issues. Bug numbers refer to the gLite Bug +Tracking system database hosted on the CERN Savannah system at Hhttps://savannah.cern.ch/bugs/?group=jra1mdwH  

 

- + - - - - - - - - - - - - - - - - - - - - - - + - +
-

Bug number

+

Bug number

+
+

Description

-

Description

+

 

 #5833

-
-

all jobs in SUBMITTED after a - job storm 

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=6722"> #6722

 #5897

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=6722">glite-job-status -all doesn't + work 

-

I20041203 LB installation - script has a missing dependency  

+
+

 

 #5910

-
-

glite-lb configuration scripts - don't set GLITE_USER environment 

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7151"> #7151

 #5925

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7151">There are conflicts when + installing WMS and LB on the same node 

-

Running glite-lb script removes - mysql.sock file  

+
+

 

 #6416

-
-

the BKserver on the LB machine - needs a symlink and the script doesn't check for it 

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7180"> #7180

 #7032

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7180">Logging & Bookkeping UI  

-

The LB installer fails with an - RPM not found message 

+
+

 

 #7152

-
-

The LB installer tries to - install gridsite with a wrong rpm name 

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7389"> #7389

 #7351

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7389">LB server and WMS local logger + related issues 

-

Star/restart of LB services  

+
+

 

 #7401

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7884"> #7884

+

The first time the LB config - script is run it fails creating the db indices 

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=7884">local header files distributed + in RPMs.  

+
+

 

 #7423

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=9861"> #9861

+

'/etc/rc.d/init.d/gLite status' - not working correctly in LB 

+ href="https://savannah.cern.ch/bugs/?func=detailitem&item_id=9861">@ext.gpt.rpm.platform@ strings + in glite-lb_installer.sh 

+
+

 

 

-

 

-
diff --git a/org.glite.deployment.lb/doc/release_notes/release_notes.pdf b/org.glite.deployment.lb/doc/release_notes/release_notes.pdf index cfd9fe519216527d0ea4f925092783b9790ab87e..422c02c486b7907ef80d1288ae14264951021124 100644 GIT binary patch delta 26873 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⁡VaSt%XW9p+;3(8}ptzjTwtG80W~$L62~79; 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 K5E6RmbpHpKxs+Z2 literal 217944 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

xgU9WWw_)riR~GH^?ZY>7C=G8~UTkFVWw2 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 diff --git a/org.glite.deployment.lb/project/build.number b/org.glite.deployment.lb/project/build.number index 58569c4..a860531 100644 --- a/org.glite.deployment.lb/project/build.number +++ b/org.glite.deployment.lb/project/build.number @@ -1,2 +1,2 @@ -#Wed Apr 13 09:36:57 CEST 2005 -module.build=232 +#Mon Oct 31 12:08:27 CET 2005 +module.build=322 diff --git a/org.glite.deployment.lb/project/glite-lb.sdf.xml.template b/org.glite.deployment.lb/project/glite-lb.sdf.xml.template index 93bb43a..b5184c8 100644 --- a/org.glite.deployment.lb/project/glite-lb.sdf.xml.template +++ b/org.glite.deployment.lb/project/glite-lb.sdf.xml.template @@ -68,17 +68,19 @@ age="@org.glite.wms-utils.exception.info.age@" build="@org.glite.wms-utils.exception.info.build@" arch="i386"/> - - - + + arch="@ext.gpt.platform@"/> + arch="@ext.myproxy.platform@"/> _installer.sh -wget -N --non-verbose +wget -N -nv if [ ! -f "" ] then echo @@ -295,7 +297,7 @@ SCRIPTLISTUn="$SCRIPTLISTUn ./ -u " -- -wget -N --non-verbose +wget -N -nv if [ ! -f "" ] then echo @@ -316,7 +318,7 @@ RPMLIST="$RPMLIST " -- -wget -N --non-verbose /RPMS/ +wget -N -nv /RPMS/ if [ ! -f "" ] then echo diff --git a/org.glite.deployment.lb/project/quattor-template.xsl b/org.glite.deployment.lb/project/quattor-template.xsl index e0bd623..cc2ad84 100644 --- a/org.glite.deployment.lb/project/quattor-template.xsl +++ b/org.glite.deployment.lb/project/quattor-template.xsl @@ -12,6 +12,7 @@ + template pro_software_glite_lb; # @@ -22,6 +23,12 @@ template pro_software_glite_lb; # glite-lb Quattor template v. # +## CAs + +include pro_software_glite_CA; + + + # Global dependencies @@ -39,6 +46,11 @@ template pro_software_glite_lb; + +include pro_software_; + + + diff --git a/org.glite.deployment.lb/project/version.properties b/org.glite.deployment.lb/project/version.properties index 66a3e69..a7d0971 100644 --- a/org.glite.deployment.lb/project/version.properties +++ b/org.glite.deployment.lb/project/version.properties @@ -1,4 +1,4 @@ -module.version = 1.3.0 +module.version = 2.0.5 module.age = 1 \ No newline at end of file diff --git a/org.glite.jp.client/.cvsignore b/org.glite.jp.client/.cvsignore deleted file mode 100644 index 3a4edf6..0000000 --- a/org.glite.jp.client/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.project diff --git a/org.glite.jp.client/Makefile b/org.glite.jp.client/Makefile deleted file mode 100644 index 345a7c1..0000000 --- a/org.glite.jp.client/Makefile +++ /dev/null @@ -1,135 +0,0 @@ -# defaults -top_srcdir=. -builddir=build -top_builddir=${top_srcdir}/${builddir} -stagedir=. -distdir=. -globalprefix=glite -jpprefix=jp -package=glite-jp-client -version=0.0.0 -PREFIX=/opt/glite - -glite_location=/opt/glite -globus_prefix=/opt/globus -nothrflavour=gcc32 -thrflavour=gcc32pthr -expat_prefix=/opt/expat -ares_prefix=/opt/ares -gsoap_prefix=/software/gsoap-2.6 - -CC=gcc - --include Makefile.inc - - -VPATH=${top_srcdir}/src:${top_srcdir}/examples:${top_srcdir}/project:${stagedir}/interface - -GLOBUS_LIBS:=-L${globus_prefix}/lib \ - -lglobus_common_${nothrflavour} \ - -lglobus_gssapi_gsi_${nothrflavour} - -GLOBUS_CFLAGS:=-I${globus_prefix}/include/${nothrflavour} - -DEBUG:=-g -O0 -DDEBUG - -CFLAGS:=${DEBUG} -I. -I${top_srcdir}/interface -I${top_srcdir}/src -I${gsoap_prefix}/include -I${stagedir}/include ${GLOBUS_CFLAGS} -LDFLAGS:=-L${stagedir}/lib - -LINK:=libtool --mode=link ${CC} ${LDFLAGS} -LTCOMPILE:=libtool --mode=compile ${CC} ${CFLAGS} -LINKXX:=libtool --mode=link ${CXX} ${LDFLAGS} -INSTALL:=libtool --mode=install install - -daemon:=glite-jp-importer - -wsprefix:=jpps_ - -SRCS:= jpimporter.c \ - ${wsprefix}ClientLib.c ${wsprefix}C.c -# env_C.c - -EXA_SRCS:= - -OBJS:=${SRCS:.c=.o} -EXA_OBJS:=${EXA_SRCS:.c=.o} - -COMMONLIB:=-lglite_jp_common -GSOAPLIB:=-lglite_security_gsoap_plugin_${nothrflavour} -lglite_security_gss_${nothrflavour} \ - -L${gsoap_prefix}/lib -lgsoap${GSOAP_DEBUG} -L${ares_prefix}/lib -lares -LBMAILDIRLIB:=-lglite_lb_maildir - -default all: compile - -compile: ${daemon} - -${daemon}: ${OBJS} - ${LINK} -o $@ ${OBJS} ${LBMAILDIRLIB} ${COMMONLIB} ${GSOAPLIB} ${GLOBUS_LIBS} - - -JobProvenancePS.xh: %.xh: %.wsdl JobProvenanceTypes.wsdl typemap.dat - cp ${stagedir}/interface/JobProvenanceTypes.wsdl . - ${gsoap_prefix}/bin/wsdl2h -t ${top_srcdir}/src/typemap.dat -c -o $@ $< - rm -f JobProvenanceTypes.wsdl - -${wsprefix}Client.c ${wsprefix}ClientLib.c \ -${wsprefix}C.c ${wsprefix}H.h: JobProvenancePS.xh - ${gsoap_prefix}/bin/soapcpp2 -n -w -c -p ${wsprefix} JobProvenancePS.xh - -env_C.c env_Server.c: - touch env.xh - cp ${jpproject}/JobProvenanceTypes.wsdl . - ${gsoap_prefix}/bin/wsdl2h -t ${top_srcdir}/src/typemap.dat -c -o env.xh JobProvenanceTypes.wsdl - rm -f JobProvenanceTypes.wsdl - ${gsoap_prefix}/bin/soapcpp2 -w -c -p env_ env.xh - -${OBJS}: ${wsprefix}H.h soap_version.h - -soap_version.h: - ${gsoap_prefix}/bin/soapcpp2 /dev/null - perl -ne '$$. == 2 && /.*([0-9])\.([0-9])\.([0-9]).*/ && printf "#define GSOAP_VERSION %d%02d%02d\n",$$1,$$2,$$3' soapH.h >$@ - -rm soapC.cpp soapH.h soapStub.h soapClient.cpp soapServer.cpp soapClientLib.cpp soapServerLib.cpp - - - - -check: - -echo nothing yet - -doc: - -stage: compile - ${MAKE} PREFIX=${stagedir} DOSTAGE=yes install - -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 ${PREFIX}/etc ${PREFIX}/examples ${PREFIX}/etc/init.d - ${INSTALL} -m 755 ${daemon} ${PREFIX}/bin - -clean: - -# we have no real config.h but have to force gSoap not to use -# linux ftime with broken (aka obsolete) DST information -stdsoap2.o: ${gsoap_prefix}/devel/stdsoap2.c - test -f config.h || touch config.h - @echo 'The following warning "time_t (de)serialization is not MT safe on this platform" is harmless' - ${CC} -o $@ -c -DWITH_NONAMESPACES -DHAVE_CONFIG_H ${CFLAGS} ${gsoap_prefix}/devel/stdsoap2.c - - -%.lo: %.c - ${LTCOMPILE} -o $@ -c $< - -%.o: %.c - ${LTCOMPILE} -o $@ -c $< diff --git a/org.glite.jp.client/build.xml b/org.glite.jp.client/build.xml deleted file mode 100755 index 8a40155..0000000 --- a/org.glite.jp.client/build.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.client/project/build.number b/org.glite.jp.client/project/build.number deleted file mode 100644 index d794048..0000000 --- a/org.glite.jp.client/project/build.number +++ /dev/null @@ -1 +0,0 @@ -module.build=0 diff --git a/org.glite.jp.client/project/build.properties b/org.glite.jp.client/project/build.properties deleted file mode 100644 index e69de29..0000000 diff --git a/org.glite.jp.client/project/configure.properties.xml b/org.glite.jp.client/project/configure.properties.xml deleted file mode 100644 index 3744be5..0000000 --- a/org.glite.jp.client/project/configure.properties.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - -top_srcdir=.. -builddir=build -stagedir=${stage.abs.dir} -distdir=${dist.dir} -globalprefix=${global.prefix} -lbprefix=${subsystem.prefix} -package=${module.package.name} -PREFIX=${install.dir} -version=${module.version} -glite_location=${with.glite.location} -globus_prefix=${with.globus.prefix} -expat_prefix=${with.expat.prefix} -ares_prefix=${with.ares.prefix} -gsoap_prefix=${with.gsoap.prefix} -mysql_prefix=${with.mysql.prefix} -mysql_version=${ext.mysql.version} -thrflavour=${with.globus.thr.flavor} -nothrflavour=${with.globus.nothr.flavor} -cppunit=${with.cppunit.prefix} -jpproject=${subsystem.project.dir} -project=${component.project.dir} - - - diff --git a/org.glite.jp.client/project/properties.xml b/org.glite.jp.client/project/properties.xml deleted file mode 100755 index e2a32d0..0000000 --- a/org.glite.jp.client/project/properties.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.client/project/tar_exclude b/org.glite.jp.client/project/tar_exclude deleted file mode 100644 index e1fcd1a..0000000 --- a/org.glite.jp.client/project/tar_exclude +++ /dev/null @@ -1,10 +0,0 @@ -tar_exclude -CVS -build.xml -build -build.properties -properties.xml -configure.properties.xml -.cvsignore -.project -.cdtproject diff --git a/org.glite.jp.client/project/version.properties b/org.glite.jp.client/project/version.properties deleted file mode 100644 index cd1e9e7..0000000 --- a/org.glite.jp.client/project/version.properties +++ /dev/null @@ -1,2 +0,0 @@ -module.version=1.0.0 -module.age=1 diff --git a/org.glite.jp.client/src/authz.c b/org.glite.jp.client/src/authz.c deleted file mode 100644 index 3e6d6e4..0000000 --- a/org.glite.jp.client/src/authz.c +++ /dev/null @@ -1,76 +0,0 @@ -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "jpps_H.h" - -int glite_jpps_authz(glite_jp_context_t ctx,int op,const char *job,const char *owner) -{ - glite_jp_error_t err; - char buf[200]; - int i; - - memset(&err,0,sizeof err); - glite_jp_clear_error(ctx); - err.source = __FUNCTION__; - err.code = EPERM; - - switch (op) { - case SOAP_TYPE___jpsrv__RegisterJob: - case SOAP_TYPE___jpsrv__StartUpload: - case SOAP_TYPE___jpsrv__CommitUpload: - for (i=0; ctx->trusted_peers && ctx->trusted_peers[i]; i++) - if (!strcmp(ctx->trusted_peers[i],ctx->peer)) return 0; - err.desc = "you are not a trusted peer"; - return glite_jp_stack_error(ctx,&err); - - case SOAP_TYPE___jpsrv__GetJob: - assert(owner); - return strcmp(owner,ctx->peer) ? glite_jp_stack_error(ctx,&err) : 0; - break; - - default: - snprintf(buf,sizeof buf,"%d: unknown operation",op); - err.desc = buf; - err.code = EINVAL; - return glite_jp_stack_error(ctx,&err); - } -} - -int glite_jpps_readauth(glite_jp_context_t ctx,const char *file) -{ - FILE *f = fopen(file,"r"); - glite_jp_error_t err; - int cnt = 0; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - if (!f) { - err.code = errno; - err.desc = file; - return glite_jp_stack_error(ctx,&err); - } - - ctx->trusted_peers = NULL; - while (!feof(f)) { - char buf[BUFSIZ]; - - if (fscanf(f,"%[^\n]\n",buf) != 1) { - err.code = EINVAL; - err.desc = file; - fclose(f); - return glite_jp_stack_error(ctx,&err); - } - - ctx->trusted_peers = realloc(ctx->trusted_peers, (cnt+1) * sizeof *ctx->trusted_peers); - ctx->trusted_peers[cnt++] = strdup(buf); - ctx->trusted_peers[cnt] = NULL; - } - fclose(f); - return 0; -} diff --git a/org.glite.jp.client/src/authz.h b/org.glite.jp.client/src/authz.h deleted file mode 100644 index 9451aef..0000000 --- a/org.glite.jp.client/src/authz.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Check authorisation of JPPS operation on job. - * - * \param[in] ctx JP context including peer name & other credentials (VOMS etc.) - * \param[in] op operation, one of SOAP_TYPE___jpsrv__* - * \param[in] job jobid of the job to decide upon - * \param[in] owner current known owner of the job (may be NULL), shortcut to avoid - * unnecessary database query. - * - * \retval 0 OK, operation permitted - * \retval EPERM denied - * \retval other error - */ - -int glite_jpps_authz(glite_jp_context_t ctx,int op,const char *job,const char *owner); - -int glite_jpps_readauth(glite_jp_context_t ctx,const char *file); - diff --git a/org.glite.jp.client/src/backend.h b/org.glite.jp.client/src/backend.h deleted file mode 100644 index cf901fb..0000000 --- a/org.glite.jp.client/src/backend.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef __GLITE_JP_BACKEND -#define __GLITE_JP_BACKEND - -#include -#include - -int glite_jppsbe_init( - glite_jp_context_t ctx, - int argc, - char *argv[] -); - -int glite_jppsbe_init_slave( - glite_jp_context_t ctx -); - -int glite_jppsbe_register_job( - glite_jp_context_t ctx, - const char *job, - const char *owner -); - -int glite_jppsbe_start_upload( - glite_jp_context_t ctx, - const char *job, - const char *class, /* must be filesystem-friendly */ - const char *name, /* optional name within the class */ - const char *content_type, - char **destination_out, - time_t *commit_before_inout -); - -int glite_jppsbe_commit_upload( - glite_jp_context_t ctx, - const char *destination -); - -int glite_jppsbe_get_names( - glite_jp_context_t ctx, - const char *job, - const char *class, - char ***names_out -); - -int glite_jppsbe_destination_info( - glite_jp_context_t ctx, - const char *destination, - char **job_out, - char **class_out, - char **name_out -); - -int glite_jppsbe_get_job_url( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* optional within class */ - char **url_out -); - -int glite_jppsbe_open_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* optional within class */ - int mode, - void **handle_out -); - -int glite_jppsbe_close_file( - glite_jp_context_t ctx, - void *handle -); - -int glite_jppsbe_pread( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset, - ssize_t *nbytes_ret -); - -int glite_jppsbe_pwrite( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset -); - -int glite_jppsbe_append( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes -); - -int glite_jppsbe_get_job_metadata( - glite_jp_context_t ctx, - const char *job, - glite_jp_attrval_t attrs_inout[] -); - -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -); - -#endif diff --git a/org.glite.jp.client/src/bones_server.c b/org.glite.jp.client/src/bones_server.c deleted file mode 100644 index 8a47169..0000000 --- a/org.glite.jp.client/src/bones_server.c +++ /dev/null @@ -1,327 +0,0 @@ -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "glite/lb/srvbones.h" -#include "glite/security/glite_gss.h" - -#include -#include "glite/security/glite_gsplugin.h" - -#include "backend.h" -#include "file_plugin.h" - -#include "soap_version.h" -#include "jpps_H.h" - -#define CONN_QUEUE 20 - -extern SOAP_NMAC struct Namespace jpis__namespaces[],jpps__namespaces[]; - -static int newconn(int,struct timeval *,void *); -static int request(int,struct timeval *,void *); -static int reject(int); -static int disconn(int,struct timeval *,void *); -static int data_init(void **data); - -static struct glite_srvbones_service stab = { - "JP Primary Storage", -1, newconn, request, reject, disconn -}; - -static time_t cert_mtime; -static char *server_cert, *server_key, *cadir; -static gss_cred_id_t mycred = GSS_C_NO_CREDENTIAL; -static char *mysubj; - -static char *port = "8901"; -static int debug = 1; - -static glite_jp_context_t ctx; - -static int call_opts(glite_jp_context_t,char *,char *,int (*)(glite_jp_context_t,int,char **)); - -char *glite_jp_default_namespace; - -int main(int argc, char *argv[]) -{ - int one = 1,opt,i; - edg_wll_GssStatus gss_code; - struct sockaddr_in a; - char *b_argv[20] = { "backend" },*p_argv[20] = { "plugins" },*com; - int b_argc,p_argc; - - glite_jp_init_context(&ctx); - - b_argc = p_argc = 1; - - while ((opt = getopt(argc,argv,"B:P:a:")) != EOF) switch (opt) { - case 'B': - assert(b_argc < 20); - if (com = strchr(optarg,',')) *com = 0; - - /* XXX: memleak -- who cares for once */ - asprintf(&b_argv[b_argc++],"-%s",optarg); - if (com) b_argv[b_argc++] = com+1; - - break; - case 'P': - assert(p_argc < 20); - p_argv[p_argc++] = optarg; - - break; - case 'a': - if (glite_jpps_readauth(ctx,optarg)) { - fprintf(stderr,"%s: %s\n",argv[0],glite_jp_error_chain(ctx)); - exit (1); - } - break; - case '?': fprintf(stderr,"usage: %s: -Bb,val ... -Pplugin.so ...\n" - "b is backend option\n",argv[0]); - exit (1); - } - - if (b_argc == 1) { - fputs("-B required\n",stderr); - exit (1); - } - - optind = 0; /* XXX: getopt used internally */ - if (glite_jppsbe_init(ctx,b_argc,b_argv)) { - fputs(glite_jp_error_chain(ctx), stderr); - exit(1); - } - - optind = 0; /* XXX: getopt used internally */ - if (b_argc > 1 && glite_jpps_fplug_load(ctx,p_argc,p_argv)) { - fputs(glite_jp_error_chain(ctx), stderr); - exit(1); - } - - srand48(time(NULL)); /* feed id generation */ - -#if GSOAP_VERSION <= 20602 - for (i=0; jpps__namespaces[i].id && strcmp(jpps__namespaces[i].id,"ns1"); i++); -#else - for (i=0; jpps__namespaces[i].id && strcmp(jpps__namespaces[i].id,"jpsrv"); i++); -#endif - assert(jpps__namespaces[i].id); - glite_jp_default_namespace = jpps__namespaces[i].ns; - - stab.conn = socket(PF_INET, SOCK_STREAM, 0); - if (stab.conn < 0) { - perror("socket"); - return 1; - } - - setsockopt(stab.conn,SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - - a.sin_family = AF_INET; - a.sin_addr.s_addr = INADDR_ANY; - a.sin_port = htons(atoi(port)); - if (bind(stab.conn,(struct sockaddr *) &a, sizeof(a)) ) { - char buf[200]; - - snprintf(buf,sizeof(buf),"bind(%d)",atoi(port)); - perror(buf); - return 1; - } - - if (listen(stab.conn,CONN_QUEUE)) { - perror("listen()"); - return 1; - } - - if (!server_cert || !server_key) - fprintf(stderr, "%s: WARNING: key or certificate file not specified, " - "can't watch them for changes\n", - argv[0]); - - if ( cadir ) setenv("X509_CERT_DIR", cadir, 1); - edg_wll_gss_watch_creds(server_cert, &cert_mtime); - - if ( !edg_wll_gss_acquire_cred_gsi(server_cert, server_key, &mycred, &mysubj, &gss_code)) - fprintf(stderr,"Server idenity: %s\n",mysubj); - else fputs("WARNING: Running unauthenticated\n",stderr); - - /* XXX: daemonise */ - - glite_srvbones_set_param(GLITE_SBPARAM_SLAVES_COUNT,1); - glite_srvbones_run(data_init,&stab,1 /* XXX: entries in stab */,debug); - - return 0; -} - -static int data_init(void **data) -{ - *data = (void *) soap_new(); - - printf("[%d] slave started\n",getpid()); - glite_jppsbe_init_slave(ctx); /* XXX: global but slave's */ - - return 0; -} - -static int newconn(int conn,struct timeval *to,void *data) -{ - struct soap *soap = (struct soap *) data; - glite_gsplugin_Context plugin_ctx; - - gss_cred_id_t newcred = GSS_C_NO_CREDENTIAL; - edg_wll_GssStatus gss_code; - gss_name_t client_name = GSS_C_NO_NAME; - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - OM_uint32 maj_stat,min_stat; - - - int ret = 0; - - soap_init2(soap,SOAP_IO_KEEPALIVE,SOAP_IO_KEEPALIVE); - soap_set_namespaces(soap,jpps__namespaces); - soap->user = (void *) ctx; /* XXX: one instance per slave */ - -/* not yet: client to JP index - ctx->other_soap = soap_new(); - soap_init(ctx->other_soap); - soap_set_namespaces(ctx->other_soap,jpis__namespaces); -*/ - - - glite_gsplugin_init_context(&plugin_ctx); - plugin_ctx->connection = calloc(1,sizeof *plugin_ctx->connection); - soap_register_plugin_arg(soap,glite_gsplugin,plugin_ctx); - - switch (edg_wll_gss_watch_creds(server_cert,&cert_mtime)) { - case 0: break; - case 1: if (!edg_wll_gss_acquire_cred_gsi(server_cert,server_key, - &newcred,NULL,&gss_code)) - { - - printf("[%d] reloading credentials\n",getpid()); /* XXX: log */ - gss_release_cred(&min_stat,&mycred); - mycred = newcred; - } - break; - case -1: - printf("[%d] edg_wll_gss_watch_creds failed\n", getpid()); /* XXX: log */ - break; - } - - /* TODO: DNS paranoia etc. */ - - if (edg_wll_gss_accept(mycred,conn,to,plugin_ctx->connection,&gss_code)) { - printf("[%d] GSS connection accept failed, closing.\n", getpid()); - ret = 1; - goto cleanup; - } - - maj_stat = gss_inquire_context(&min_stat,plugin_ctx->connection->context, - &client_name, NULL, NULL, NULL, NULL, NULL, NULL); - - if (!GSS_ERROR(maj_stat)) - maj_stat = gss_display_name(&min_stat,client_name,&token,NULL); - - if (ctx->peer) free(ctx->peer); - if (!GSS_ERROR(maj_stat)) { - printf("[%d] client DN: %s\n",getpid(),(char *) token.value); /* XXX: log */ - - ctx->peer = strdup(token.value); - memset(&token, 0, sizeof(token)); - } - else { - printf("[%d] annonymous client\n",getpid()); - ctx->peer = NULL; - } - - if (client_name != GSS_C_NO_NAME) gss_release_name(&min_stat, &client_name); - if (token.value) gss_release_buffer(&min_stat, &token); - - return 0; - -cleanup: - glite_gsplugin_free_context(plugin_ctx); - soap_end(soap); - - return ret; -} - -static int request(int conn,struct timeval *to,void *data) -{ - struct soap *soap = data; - glite_jp_context_t ctx = soap->user; - - glite_gsplugin_set_timeout(glite_gsplugin_get_context(soap),to); - -/* FIXME: does not work, ask nykolas */ - soap->max_keep_alive = 1; /* XXX: prevent gsoap to close connection */ - soap_begin(soap); - if (soap_begin_recv(soap)) { - if (soap->error < SOAP_STOP) { - soap_send_fault(soap); - return EIO; - } - return ENOTCONN; - } - - if (soap_envelope_begin_in(soap) - || soap_recv_header(soap) - || soap_body_begin_in(soap) - || jpps__serve_request(soap) -#if GSOAP_VERSION >= 20700 - || (soap->fserveloop && soap->fserveloop(soap)) -#endif - ) - { - soap_send_fault(soap); - return ctx->error->code; /* XXX: shall we die on some errors? */ - } - - glite_jp_run_deferred(ctx); - return 0; -} - -static int reject(int conn) -{ - int flags = fcntl(conn, F_GETFL, 0); - - fcntl(conn,F_SETFL,flags | O_NONBLOCK); - edg_wll_gss_reject(conn); - - return 0; -} - -static int disconn(int conn,struct timeval *to,void *data) -{ - struct soap *soap = (struct soap *) data; - soap_end(soap); // clean up everything and close socket - - return 0; -} - -#define WSPACE "\t\n " - -static int call_opts(glite_jp_context_t ctx,char *opt,char *name,int (*f)(glite_jp_context_t,int,char **)) -{ - int ac = 1,ret,my_optind; - char **av = malloc(sizeof *av),*ap; - - *av = name; - for (ap = strtok(opt,WSPACE); ap; ap = strtok(NULL,WSPACE)) { - av = realloc(av,(ac+1) * sizeof *av); - av[ac++] = ap; - } - - my_optind = optind; - optind = 0; - ret = f(ctx,ac,av); - optind = my_optind; - free(av); - return ret; -} - - -/* XXX: we don't use it */ -SOAP_NMAC struct Namespace namespaces[] = { {NULL,NULL} }; diff --git a/org.glite.jp.client/src/builtin_plugins.h b/org.glite.jp.client/src/builtin_plugins.h deleted file mode 100644 index 3b2c201..0000000 --- a/org.glite.jp.client/src/builtin_plugins.h +++ /dev/null @@ -1,7 +0,0 @@ - -#define GLITE_JP_FILETYPE_TAGS "urn:org.glite.jp.primary:tags" -#define GLITE_JP_FILETYPE_LB "urn:org.glite.jp.primary:lb" -#define GLITE_JP_FILETYPE_ISB "urn:org.glite.jp.primary:isb" -#define GLITE_JP_FILETYPE_OSB "urn:org.glite.jp.primary:osb" - -#define GLITE_JP_FPLUG_TAGS_APPEND 0 diff --git a/org.glite.jp.client/src/db.h b/org.glite.jp.client/src/db.h deleted file mode 100644 index 0b9f730..0000000 --- a/org.glite.jp.client/src/db.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef _DB_H -#define _DB_H - -#ident "$Header$" - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef struct _glite_jp_db_stmt_t *glite_jp_db_stmt_t; - -int glite_jp_db_connect( - glite_jp_context_t, /* INOUT: */ - char * /* IN: connect string user/password@host:database */ -); - -void glite_jp_db_close(glite_jp_context_t); - - -/* Parse and execute SQL statement. Returns number of rows selected, created - * or affected by update, or -1 on error */ - -int glite_jp_db_execstmt( - glite_jp_context_t, /* INOUT: */ - char *, /* IN: SQL statement */ - glite_jp_db_stmt_t * /* OUT: statement handle. Usable for - select only */ -); - - -/* Fetch next row of select statement. - * All columns are returned as fresh allocated strings - * - * return values: - * >0 - number of fields of the retrieved row - * 0 - no more rows - * -1 - error - * - * Errors are stored in context passed to previous glite_jp_db_execstmt() */ - -int glite_jp_db_fetchrow( - glite_jp_db_stmt_t, /* IN: statement */ - char ** /* OUT: array of fetched values. - * As number of columns is fixed and known, - * expects allocated array of pointers here */ -); - -/* Retrieve column names of a query statement */ - -int glite_jp_db_querycolumns( - glite_jp_db_stmt_t, /* IN: statement */ - char ** /* OUT: result set column names. Expects allocated array. */ -); - -/* Free the statement structure */ - -void glite_jp_db_freestmt( - glite_jp_db_stmt_t * /* INOUT: statement */ -); - - -/* convert time_t into database-specific time string - * returns pointer to static area that is changed by subsequent calls */ - -char *glite_jp_db_timetodb(time_t); -time_t glite_jp_db_dbtotime(char *); - - -/** - * Check database version. - */ -int glite_jp_db_dbcheckversion(glite_jp_context_t); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/org.glite.jp.client/src/feed.c b/org.glite.jp.client/src/feed.c deleted file mode 100644 index 5d39565..0000000 --- a/org.glite.jp.client/src/feed.c +++ /dev/null @@ -1,327 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/strmd5.h" -#include "feed.h" -#include "file_plugin.h" -#include "builtin_plugins.h" - - -/* - * seconds before feed expires: should be - * XXX: should be configurable, default for real deployment sort of 1 hour - */ -#define FEED_TTL 120 - -static int check_qry_item( - glite_jp_context_t ctx, - const glite_jp_query_rec_t *qry, - const glite_jp_attrval_t *attr -) -{ - int cmp,cmp2; - long scmp,ucmp; - - switch (qry->attr.type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TAG: - cmp = strcmp(attr->value.s,qry->value.s); - break; - case GLITE_JP_ATTR_TIME: - scmp = (ucmp = attr->value.time.tv_usec - qry->value.time.tv_usec) > 0 ? 0 : -1; - ucmp -= 1000000 * scmp; - scmp += attr->value.time.tv_sec - qry->value.time.tv_sec; - cmp = scmp ? scmp : ucmp; - break; - } - switch (qry->op) { - case GLITE_JP_QUERYOP_EQUAL: return !cmp; - case GLITE_JP_QUERYOP_UNEQUAL: return cmp; - case GLITE_JP_QUERYOP_LESS: return cmp < 0; - case GLITE_JP_QUERYOP_GREATER: return cmp > 0; - - case GLITE_JP_QUERYOP_WITHIN: - switch (qry->attr.type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TAG: - cmp2 = strcmp(attr->value.s,qry->value2.s); - break; - case GLITE_JP_ATTR_TIME: - scmp = (ucmp = attr->value.time.tv_usec - qry->value2.time.tv_usec) > 0 ? 0 : -1; - ucmp -= 1000000 * scmp; - scmp += attr->value.time.tv_sec - qry->value2.time.tv_sec; - cmp2 = scmp ? scmp : ucmp; - break; - } - return cmp >= 0 && cmp2 <= 0; - } -} - -/* XXX: limit on query size -- I'm lazy to malloc() */ -#define QUERY_MAX 100 - -static int match_feed( - glite_jp_context_t ctx, - const struct jpfeed *feed, - const char *job, - const glite_jp_attrval_t attrs[] /* XXX: not checked for correctness */ -) -{ - int i; - int attri[GLITE_JP_ATTR__LAST]; - int qi[QUERY_MAX]; - - glite_jp_attrval_t *newattr = NULL; - - glite_jp_clear_error(ctx); - - for (i=0; iqry) { - int j,complete = 1; - - memset(qi,0,sizeof qi); - for (i=0; feed->qry[i].attr.type; i++) { - assert(iqry[i].attr.type]) >=0) { - if (check_qry_item(ctx,feed->qry+i,attrs+j)) - qi[i] = 1; /* matched */ - else return 0; /* can't be satisfied */ - } - else complete = 0; - } - - /* not all attributes in query are known from input - * we have to retrieve job metadata from the backend - */ - if (!complete) { - glite_jp_attrval_t meta[GLITE_JP_ATTR__LAST+1]; - int qai[GLITE_JP_ATTR__LAST]; - - memset(meta,0,sizeof meta); - j=0; - for (i=0; feed->qry[i].attr.type; i++) if (!qi[i]) { - meta[j].attr.type = feed->qry[i].attr.type; - meta[j].attr.name = feed->qry[i].attr.name; - qai[feed->qry[i].attr.type] = i; - j++; - } - - if (glite_jppsbe_get_job_metadata(ctx,job,meta)) { - glite_jp_error_t err; - memset(&err,0,sizeof err); - err.code = EIO; - err.source = __FUNCTION__; - err.desc = "complete query"; - return glite_jp_stack_error(ctx,&err); - } - - for (i=0; j=meta[i].attr.type; i++) - if (!check_qry_item(ctx,feed->qry+qai[j],meta+i)) - return 0; - } - } - - /* matched completely */ - return glite_jpps_single_feed(ctx,feed->destination,job,attrs); - return 0; -} - -int glite_jpps_match_attr( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t attrs[] -) -{ - struct jpfeed *f = (struct jpfeed *) ctx->feeds; - int i,j; - int attri[GLITE_JP_ATTR__LAST]; - - glite_jp_clear_error(ctx); - - for (i=0; i= GLITE_JP_ATTR__LAST || - attrs[i].attr.type <= 0) - { - glite_jp_error_t err; - memset(&err,0,sizeof err); - err.code = EINVAL; - err.source = __FUNCTION__; - err.desc = "unknown attribute"; - return glite_jp_stack_error(ctx,&err); - } - if (attri[attrs[i].attr.type] >= 0) { - glite_jp_error_t err; - memset(&err,0,sizeof err); - err.code = EINVAL; - err.source = __FUNCTION__; - err.desc = "double attribute change"; - return glite_jp_stack_error(ctx,&err); - } - - attri[attrs[i].attr.type] = i; - } - - for (;f; f = f->next) { - for (i=0; f->attrs[i].type && attri[f->attrs[i].type] == -1; i++); - /* XXX: ignore any errors */ - if (f->attrs[i].type) match_feed(ctx,f,job,attrs); - } - - return glite_jp_clear_error(ctx); -} - -int glite_jpps_match_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name -) -{ - glite_jpps_fplug_data_t **pd = NULL; - int pi; - void *bh = NULL; - int ret; - - fprintf(stderr,"%s: %s %s %s\n",__FUNCTION__,job,class,name); - - switch (glite_jpps_fplug_lookup(ctx,class,&pd)) { - case ENOENT: return 0; /* XXX: shall we complain? */ - case 0: break; - default: return -1; - } - - for (pi=0; pd[pi]; pi++) { - int ci; - for (ci=0; pd[pi]->uris[ci]; ci++) if (!strcmp(pd[pi]->uris[ci],class)) { - void *ph; - - if (!bh && (ret = glite_jppsbe_open_file(ctx,job,pd[pi]->classes[ci],name,O_RDONLY,&bh))) { - free(pd); - return ret; - } - - if (pd[pi]->ops.open(pd[pi]->fpctx,bh,class,&ph)) { - /* XXX: complain more visibly */ - fputs("plugin open failed\n",stderr); - continue; - } - - /* XXX: does not belong here but I'd like to avoid opening the file twice */ - if (!strcmp(class,GLITE_JP_FILETYPE_LB)) { - glite_jp_attr_t owner = { GLITE_JP_ATTR_OWNER, NULL }; - glite_jp_attrval_t *val; - - switch (pd[pi]->ops.attr(pd[pi]->fpctx,ph,owner,&val)) { - case ENOENT: - case ENOSYS: abort(); - case 0: printf("LB plugin: owner = %s\n",val[0].value.s); - /* TODO: store it in backend */ - - glite_jp_attrval_free(val,1); - break; - - default: /* TODO: complain */; break; - } - } - - /* TODO: extract attributes for the feeds */ - - - pd[pi]->ops.close(pd[pi]->fpctx,ph); - } - } - - if (bh) glite_jppsbe_close_file(ctx,bh); - free(pd); - - return 0; -} - -int glite_jpps_match_tag( - glite_jp_context_t ctx, - const char *job, - const glite_jp_tagval_t *tag -) -{ - fprintf(stderr,"%s: \n",__FUNCTION__); - return 0; -} - -static char *generate_feedid(void) -{ - char hname[200],buf[1000]; - - gethostname(hname,sizeof hname); - snprintf(buf,sizeof buf,"%s%d%ld",hname,getpid(),lrand48()); - buf[sizeof buf-1] = 0; - return str2md5base64(buf); -} - - -int glite_jpps_run_feed( - glite_jp_context_t ctx, - const char *destination, - const glite_jp_attr_t *attrs, - const glite_jp_query_rec_t *qry, - char **feed_id) -{ - fprintf(stderr,"%s: \n",__FUNCTION__); - return 0; -} - -static int register_feed_deferred(glite_jp_context_t ctx,void *feed) -{ - struct jpfeed *f = feed; - - f->next = ctx->feeds; - ctx->feeds = f; - return 0; -} - -/* FIXME: - * - volatile implementation: should store the registrations in a file - * and recover after restart - * - should communicate the data among all server slaves - */ -int glite_jpps_register_feed( - glite_jp_context_t ctx, - const char *destination, - const glite_jp_attr_t *attrs, - const glite_jp_query_rec_t *qry, - char **feed_id, - time_t *expires) -{ - int i; - struct jpfeed *f = calloc(1,sizeof *f); - - if (!*feed_id) *feed_id = generate_feedid(); - time(expires); *expires += FEED_TTL; - - f->id = strdup(*feed_id); - f->destination = strdup(destination); - f->expires = *expires; - for (i=0; attrs[i].type; i++) { - f->attrs = realloc(f->attrs,(i+2) * sizeof *f->attrs); - glite_jp_attr_copy(f->attrs+i,attrs+i); - memset(f->attrs+i+1,0,sizeof *f->attrs); - } - for (i=0; qry[i].attr.type; i++) { - f->qry = realloc(f->qry,(i+2) * sizeof *f->qry); - glite_jp_queryrec_copy(f->qry+i,qry+i); - memset(f->qry+i+1,0,sizeof *f->qry); - } - - glite_jp_add_deferred(ctx,register_feed_deferred,f); - - return 0; -} - diff --git a/org.glite.jp.client/src/feed.h b/org.glite.jp.client/src/feed.h deleted file mode 100644 index c3c2461..0000000 --- a/org.glite.jp.client/src/feed.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __GLITE_JP_FEED -#define __GLITE_JP_FEED - - -struct jpfeed { - char *id,*destination; - time_t expires; - glite_jp_attr_t *attrs; - glite_jp_query_rec_t *qry; - struct jpfeed *next; -}; - - -int glite_jpps_match_attr(glite_jp_context_t,const char *,const glite_jp_attrval_t[]); -int glite_jpps_match_file(glite_jp_context_t,const char *,const char *,const char *); -int glite_jpps_match_tag(glite_jp_context_t,const char *,const glite_jp_tagval_t *); -int glite_jpps_run_feed(glite_jp_context_t,const char *,const glite_jp_attr_t *,const glite_jp_query_rec_t *,char **); -int glite_jpps_register_feed(glite_jp_context_t,const char *,const glite_jp_attr_t *,const glite_jp_query_rec_t *,char **,time_t *); - -#endif - diff --git a/org.glite.jp.client/src/file_plugin.c b/org.glite.jp.client/src/file_plugin.c deleted file mode 100644 index 144a231..0000000 --- a/org.glite.jp.client/src/file_plugin.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include "file_plugin.h" - -static struct option opts[] = { - { "plugin", 1, NULL, 'p' }, - { NULL } -}; - -static int loadit(glite_jp_context_t ctx,const char *so) -{ -/* XXX: not stored but we never dlclose() yet */ - void *dl_handle = dlopen(so,RTLD_NOW); - - glite_jp_error_t err; - const char *e; - glite_jpps_fplug_data_t *data,*dp; - int i; - - glite_jpps_fplug_init_t init; - memset(&err,0,sizeof err); - - if (!dl_handle) { - err.source = "dlopen()"; - err.code = EINVAL; - err.desc = dlerror(); - return glite_jp_stack_error(ctx,&err); - } - - dlerror(); - init = dlsym(dl_handle,"init"); - e = dlerror(); - if (e) { - char buf[300]; - snprintf(buf,sizeof buf,"dlsym(\"%s\",\"init\")",so); - buf[299] = 0; - err.source = buf; - err.code = ENOENT; - err.desc = e; - return glite_jp_stack_error(ctx,&err); - } - - data = calloc(1,sizeof *data); - - if (init(ctx,data)) return -1; - - i = 0; - if (ctx->plugins) for (i=0; ctx->plugins[i]; i++); - ctx->plugins = realloc(ctx->plugins, (i+2) * sizeof *ctx->plugins); - ctx->plugins[i] = data; - ctx->plugins[i+1] = NULL; - - /* TODO: check consistency of uri+class pairs wrt. previous plugins */ - - return 0; -} - -int glite_jpps_fplug_load(glite_jp_context_t ctx,int argc,char **argv) -{ - int i; - - for (i=1; iplugins) { - return glite_jp_stack_error(ctx,&err); - } - - for (i = 0; ctx->plugins[i]; i++) { - int j; - glite_jpps_fplug_data_t *p = ctx->plugins[i]; - - for (j=0; p->uris && p->uris[j]; j++) - if (!strcmp(p->uris[j],uri)) { - out = realloc(out, (matches+2) * sizeof *out); - out[matches++] = p; - out[matches] = NULL; - } - } - - if (matches) { - *plugin_data = out; - return 0; - } - else return glite_jp_stack_error(ctx,&err); -} - diff --git a/org.glite.jp.client/src/ftp_backend.c b/org.glite.jp.client/src/ftp_backend.c deleted file mode 100644 index 8bf523b..0000000 --- a/org.glite.jp.client/src/ftp_backend.c +++ /dev/null @@ -1,1744 +0,0 @@ -#ident "$Header$" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" -#include "glite/jp/strmd5.h" - -#include "tags.h" -#include "backend.h" - -#define UPLOAD_SUFFIX ".upload" -#define LOCK_SUFFIX ".lock" - -struct ftpbe_config { - char *internal_path; - char *external_path; - char *gridmap; - char *logname; -}; - -static struct ftpbe_config *config = NULL; - -struct fhandle_rec { - int fd; - int fd_append; -}; -typedef struct fhandle_rec *fhandle; - -static struct option ftpbe_opts[] = { - { "ftp-internal-path", 1, NULL, 'I' }, - { "ftp-external-path", 1, NULL, 'E' }, - { "ftp-gridmap", 1, NULL, 'G' }, - { NULL, 0, NULL, 0 } -}; - -/* obsolete */ -#if 0 -static struct { - glite_jp_fileclass_t type; - char * fname; - } class_to_fname_tab[] = { - { GLITE_JP_FILECLASS_INPUT, "input" }, - { GLITE_JP_FILECLASS_OUTPUT, "output" }, - { GLITE_JP_FILECLASS_LBLOG, "lblog" }, - { GLITE_JP_FILECLASS_TAGS, "tags" }, - { GLITE_JP_FILECLASS_UNDEF, NULL } - }; - -static char *class_to_fname(glite_jp_fileclass_t type) -{ - int i; - - for (i = 0; class_to_fname_tab[i].type != GLITE_JP_FILECLASS_UNDEF; i++) - if (type == class_to_fname_tab[i].type) - return class_to_fname_tab[i].fname; - - return NULL; -} - -static glite_jp_fileclass_t fname_to_class(char* fname) -{ - int i; - - for (i = 0; class_to_fname_tab[i].type != GLITE_JP_FILECLASS_UNDEF; i++) - if (!strcmp(fname, class_to_fname_tab[i].fname)) - return class_to_fname_tab[i].type; - - return GLITE_JP_FILECLASS_UNDEF; -} -#endif - -static int config_check( - glite_jp_context_t ctx, - struct ftpbe_config *config) -{ - return config == NULL || - config->internal_path == NULL || - config->external_path == NULL || - config->gridmap == NULL || - config->logname == NULL; - - /* XXX check reality */ -} - -static int jobid_unique_pathname(glite_jp_context_t ctx, const char *job, - char **unique, char **ju_path, int get_path) -{ - char *p; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - p = strrchr(job, '/'); - if (!p) { - err.code = EINVAL; - err.desc = "Malformed jobid"; - return glite_jp_stack_error(ctx,&err); - } - /* XXX thorough checks */ - if (!(*unique = strdup(p+1))) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - if (get_path) { - if (!(*ju_path = strdup(p+1))) { - free(*unique); - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - *(*ju_path + 10) = '\0'; - } - return 0; -} - -static int mkdirpath(const char* path, int prefixlen) -{ - char *wpath, *p; - int goout, ret; - - wpath = strdup(path); - if (!wpath) { - errno = ENOMEM; - return -1; - } - - p = wpath + prefixlen; - goout = 0; - while (!goout) { - while (*p == '/') p++; - while (*p != '/' && *p != '\0') p++; - goout = (*p == '\0'); - *p = '\0'; - ret = mkdir(wpath, S_IRUSR | S_IWUSR | S_IXUSR); - if (ret < 0 && errno != EEXIST) break; - *p = '/'; - } - free(wpath); - return goout ? 0 : ret; -} - -static long regtime_trunc(long tv_sec) -{ - return tv_sec / (86400*7); -} - -static long regtime_ceil(long tv_sec) -{ - return (tv_sec % (86400*7)) ? tv_sec/(86400*7)+1 : tv_sec/(86400*7) ; -} - -/********************************************************************************/ -int glite_jppsbe_init( - glite_jp_context_t ctx, - int argc, - char *argv[] -) -{ - glite_jp_error_t err; - int opt; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - config = (struct ftpbe_config *) calloc(1, sizeof *config); - if (!config) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - config->logname = getlogin(); - - while ((opt = getopt_long(argc, argv, "I:E:G:", ftpbe_opts, NULL)) != EOF) { - switch (opt) { - case 'I': config->internal_path = optarg; break; - case 'E': config->external_path = optarg; break; - case 'G': config->gridmap = optarg; break; - default: break; - } - } - - if (config_check(ctx, config)) { - err.code = EINVAL; - err.desc = "Invalid FTP backend configuration"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_init_slave( - glite_jp_context_t ctx -) -{ - /* Nothing to do */ -} - -int glite_jppsbe_register_job( - glite_jp_context_t ctx, - const char *job, - const char *owner -) -{ - glite_jp_error_t err; - char *int_dir = NULL; - char *int_fname = NULL; - char *data_dir = NULL; - char *data_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *ownerhash = NULL; - FILE *regfile = NULL; - struct timeval reg_tv; - long reg_tv_trunc; - struct stat statbuf; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job != NULL); - assert(owner != NULL); - - gettimeofday(®_tv, NULL); - reg_tv_trunc = regtime_trunc(reg_tv.tv_sec); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_dir, "%s/regs/%s", - config->internal_path, ju_path) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - if (mkdirpath(int_dir, strlen(config->internal_path)) < 0 && - errno != EEXIST) { - free(int_dir); - err.code = errno; - err.desc = "Cannot mkdir jobs's reg directory"; - return glite_jp_stack_error(ctx,&err); - } - free(int_dir); - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - if (stat(int_fname, &statbuf) < 0) { - if (errno != ENOENT) { - err.code = errno; - err.desc = "Cannot stat jobs's reg info file"; - goto error_out; - } - } else { - err.code = EEXIST; - err.desc = "Job already registered"; - goto error_out; - } - - regfile = fopen(int_fname, "w"); - if (regfile == NULL) { - err.code = errno; - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - - ownerhash = str2md5(owner); /* static buffer */ - - if (fprintf(regfile, "%d %ld.%06ld %s %s %d %s\n", 1, - (long)reg_tv.tv_sec, (long)reg_tv.tv_usec, job, - ownerhash, strlen(owner), owner) < 1 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot write jobs's reg info file"; - goto error_out; - } - if (fclose(regfile) != 0 ) { - err.code = errno; - err.desc = "Cannot close(write) jobs's reg info file"; - goto error_out; - } - - if (asprintf(&data_dir, "%s/data/%s/%d/%s", - config->internal_path, ownerhash, regtime_trunc(reg_tv.tv_sec), ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (asprintf(&data_fname, "%s/_info", data_dir) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (mkdirpath(data_dir, strlen(config->internal_path)) < 0 && - errno != EEXIST) { - err.code = errno; - err.desc = "Cannot mkdir jobs's data directory"; - goto error_out; - } - - if (link(int_fname, data_fname) < 0) { - err.code = errno; - err.desc = "Cannot link job's reg and data info files"; - goto error_out; - } - -error_out: - free(int_fname); - free(data_fname); - if (err.code && data_dir) rmdir(data_dir); - free(data_dir); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int add_to_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - gridmap = fopen(config->gridmap, "a"); - if (!gridmap) { - err.code = errno; - err.desc = "Cannot open gridmap file"; - return glite_jp_stack_error(ctx,&err); - } - if (fprintf(gridmap, "\"%s\" %s\n", dn, config->logname) < 6 || - ferror(gridmap)) { - err.code = EIO; - err.desc = "Cannot write to gridmap file"; - fclose(gridmap); - return glite_jp_stack_error(ctx,&err); - } - fclose(gridmap); - return 0; -} - -static int remove_from_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - char *temp_name = NULL; - FILE *temp_file = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - /* XXX */ - return 0; -} - -int glite_jppsbe_start_upload( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - const char *content_type, - char **destination_out, - time_t *commit_before_inout -) -{ - char *int_fname = NULL; - char *lock_fname = NULL; - FILE *lockfile = NULL; - FILE *regfile = NULL; - char *data_dir = NULL; - char *data_lock = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *peername = NULL; - int info_version; - long reg_time; - char ownerhash[33]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(destination_out!=NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - peername = glite_jp_peer_name(ctx); - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%*ld %*s %s ", &info_version, - ®_time, ownerhash) < 3 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - fclose(regfile); - - /* XXX authorization */ - - if (asprintf(&data_dir, "%s/data/%s/%d/%s", - config->internal_path, ownerhash, regtime_trunc(reg_time), ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(&lock_fname, "%s/%s" LOCK_SUFFIX, - data_dir, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (commit_before_inout != NULL) - *commit_before_inout = (time_t) LONG_MAX; /* XXX no timeout enforced */ - - lockfile = fopen(lock_fname, "w"); - if (lockfile == NULL) { - err.code = errno; - err.desc = "Cannot open uploads's lock file"; - goto error_out; - } - - if (fprintf(lockfile, "%ld %d %s\n", (long)*commit_before_inout, - peername ? peername : 0, - peername ? peername : "") < 1 || ferror(regfile)) { - fclose(lockfile); - err.code = errno; - err.desc = "Cannot write upload's lock file"; - goto error_out; - } - if (fclose(lockfile) != 0 ) { - err.code = errno; - err.desc = "Cannot close(write) upload's lock file"; - goto error_out; - } - - if (asprintf(destination_out, "%s/data/%s/%d/%s/%s" UPLOAD_SUFFIX, - config->external_path, ownerhash, regtime_trunc(reg_time), ju, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (add_to_gridmap(ctx, peername)) { - err.code = EIO; - err.desc = "Cannot add peer DN to ftp server authorization file"; - goto error_out; - } - -error_out: - free(int_fname); - free(data_dir); - if (err.code && data_lock) unlink(data_lock); - free(data_lock); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_commit_upload( - glite_jp_context_t ctx, - const char *destination -) -{ - size_t dest_len; - size_t suff_len; - size_t extp_len; - long commit_before; - int lockpeerlen; - char *lockpeername = NULL; - char *peername = NULL; - char *dest_rw = NULL; - char *dest_rw_suff = NULL; - char *dest_rw_lock = NULL; - FILE *lockfile = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(destination != NULL); - - suff_len = strlen(UPLOAD_SUFFIX); - dest_len = strlen(destination); - extp_len = strlen(config->external_path); - - if (dest_len < suff_len || - strcmp(UPLOAD_SUFFIX, destination + (dest_len - suff_len)) || - strncmp(destination, config->external_path, extp_len)) { - err.code = EINVAL; - err.desc = "Forged destination path"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&dest_rw_suff, "%s%s", config->internal_path, - destination + extp_len) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - dest_rw = strdup(dest_rw_suff); - if (!dest_rw) { - err.code = ENOMEM; - goto error_out; - } - *(dest_rw + (strlen(dest_rw_suff) - suff_len)) = '\0'; - - if (asprintf(&dest_rw_lock, "%s" LOCK_SUFFIX, dest_rw) == -1) { - err.code = ENOMEM; - goto error_out; - } - - lockfile = fopen(dest_rw_lock, "r"); - if (lockfile == NULL) { - err.code = errno; - err.desc = "Cannot open upload's lock file"; - goto error_out; - } - if (fscanf(lockfile, "%ld %d ", &commit_before, &lockpeerlen) < 2 || ferror(lockfile)) { - fclose(lockfile); - err.code = errno; - err.desc = "Cannot read upload's lock file"; - goto error_out; - } - if (lockpeerlen) { - lockpeername = (char*) calloc(1, lockpeerlen+1); - if (!lockpeername) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(lockpeername, lockpeerlen+1, lockfile) == NULL) { - fclose(lockfile); - err.code = errno; - err.desc = "Cannot read upload's lock file"; - goto error_out; - } - } - fclose(lockfile); - - peername = glite_jp_peer_name(ctx); - if (lockpeername && (!peername || strcmp(lockpeername, peername))) { - err.code = EPERM; - err.desc = "Upload started by client of different identity"; - goto error_out; - } - - if (rename(dest_rw_suff, dest_rw) < 0) { - err.code = errno; - err.desc = "Cannot move upload file to the final place"; - goto error_out; - } - - if (unlink(dest_rw_lock) < 0) { - err.code = errno; - err.desc = "Cannot unlink upload's lock file"; - goto error_out; - } - -error_out: - free(dest_rw); - free(dest_rw_suff); - free(dest_rw_lock); - free(peername); - free(lockpeername); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_destination_info( - glite_jp_context_t ctx, - const char *destination, - char **job, - char **class, - char **name -) -{ - size_t dest_len; - size_t suff_len; - size_t extp_len; - char *dest_rw = NULL; - char *dest_rw_suff = NULL; - char *dest_rw_info = NULL; - FILE *infofile = NULL; - char *classname = NULL; - char jobstr[256+1]; - glite_jp_error_t err; - - assert(destination != NULL); - assert(job != NULL); - assert(class != NULL); - assert(name != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - suff_len = strlen(UPLOAD_SUFFIX); - dest_len = strlen(destination); - extp_len = strlen(config->external_path); - - if (dest_len < suff_len || - strcmp(UPLOAD_SUFFIX, destination + (dest_len - suff_len)) || - strncmp(destination, config->external_path, extp_len)) { - err.code = EINVAL; - err.desc = "Forged destination path"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&dest_rw_suff, "%s%s", config->internal_path, - destination + extp_len) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - dest_rw = strdup(dest_rw_suff); - if (!dest_rw) { - err.code = ENOMEM; - goto error_out; - } - *(dest_rw + (strlen(dest_rw_suff) - suff_len)) = '\0'; - - classname = strrchr(dest_rw,'/'); - if (classname == NULL) { - err.code = EINVAL; - err.desc = "Forged destination path"; - goto error_out; - } - *classname++ ='\0'; - *class = strdup(classname); - -/* XXX: do we need similar check? - if (!class == GLITE_JP_FILECLASS_UNDEF) { - err.code = EINVAL; - err.desc = "Forged destination path"; - goto error_out; - } -*/ - - /* TODO: */ - *name = NULL; - - if (asprintf(&dest_rw_info, "%s/_info", dest_rw) == -1) { - err.code = ENOMEM; - goto error_out; - } - - infofile = fopen(dest_rw_info, "r"); - if (infofile == NULL) { - err.code = errno; - err.desc = "Cannot open _info file"; - goto error_out; - } - if (fscanf(infofile, "%*d %*ld.%*ld %256s ", jobstr) < 1 || ferror(infofile)) { - fclose(infofile); - err.code = errno; - err.desc = "Cannot read _info file"; - goto error_out; - } - *job = strdup(jobstr); - fclose(infofile); - -error_out: - free(dest_rw); - free(dest_rw_suff); - free(dest_rw_info); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - - -int glite_jppsbe_get_job_url( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - char **url_out -) -{ - FILE *regfile = NULL; - char *int_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - int info_version; - long reg_time; - char ownerhash[33]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(url_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%*ld %*s %s", &info_version, - ®_time, ownerhash) < 3 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - fclose(regfile); - - if (asprintf(url_out, "%s/data/%s/%d/%s/%s", - config->external_path, ownerhash, regtime_trunc(reg_time), ju, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - free(int_fname); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_fname( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - char **fname_out -) -{ - FILE *regfile = NULL; - char *int_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - int info_version; - long reg_time; - char ownerhash[33]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(fname_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%*ld %*s %s", &info_version, - ®_time, ownerhash) < 3 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - fclose(regfile); - - if (asprintf(fname_out, "%s/data/%s/%d/%s/%s", - config->internal_path, ownerhash, regtime_trunc(reg_time), ju, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - free(int_fname); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_open_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - int mode, - void **handle_out -) -{ - fhandle handle = NULL; - char* fname = NULL; - glite_jp_error_t err; - - assert(handle_out != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (get_job_fname(ctx, job, class, name, &fname)) { - err.code = ctx->error->code; - err.desc = "Cannot construct internal filename"; - return glite_jp_stack_error(ctx,&err); - } - - handle = (fhandle) calloc(1,sizeof(*handle)); - if (handle == NULL) { - err.code = ENOMEM; - goto error_out; - } - - handle->fd = open(fname, mode, S_IRUSR | S_IWUSR); - if (handle->fd < 0) { - err.code = errno; - err.desc = "Cannot open requested file"; - free(handle); - goto error_out; - } - handle->fd_append = open(fname, mode | O_APPEND, S_IRUSR | S_IWUSR); - if (handle->fd_append < 0) { - err.code = errno; - err.desc = "Cannot open requested file for append"; - close(handle->fd); - free(handle); - goto error_out; - } - *handle_out = (void*) handle; - -error_out: - free(fname); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_close_file( - glite_jp_context_t ctx, - void *handle -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (close(((fhandle)handle)->fd_append) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor (fd_append)"; - goto error_out; - } - if (close(((fhandle)handle)->fd) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor"; - goto error_out; - } - -error_out: - free(handle); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_pread( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset, - ssize_t *nbytes_ret -) -{ - ssize_t ret; - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if ((ret = pread(((fhandle)handle)->fd, buf, nbytes, offset)) < 0) { - err.code = errno; - err.desc = "Error in pread()"; - return glite_jp_stack_error(ctx,&err); - } - *nbytes_ret = ret; - - return 0; -} - -int glite_jppsbe_pwrite( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (pwrite(((fhandle)handle)->fd, buf, nbytes, offset) < 0) { - err.code = errno; - err.desc = "Error in pwrite()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_append( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (write(((fhandle)handle)->fd_append, buf, nbytes) < 0) { - err.code = errno; - err.desc = "Error in write()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -static int get_job_info( - glite_jp_context_t ctx, - const char *job, - char **owner, - struct timeval *tv_reg -) -{ - char *ju = NULL; - char *ju_path = NULL; - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char *int_fname = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %*s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, &ownerlen) < 4 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - free(int_fname); - free(ju); - free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_info_int( - glite_jp_context_t ctx, - const char *int_fname, - char **jobid, - char **owner, - struct timeval *tv_reg -) -{ - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char jobid_buf[256]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, jobid_buf, &ownerlen) < 5 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - *jobid = strdup(jobid_buf); - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_get_job_metadata( - glite_jp_context_t ctx, - const char *job, - glite_jp_attrval_t attrs_inout[] -) -{ - int got_info = 0; - struct timeval tv_reg; - char *owner = NULL; - int got_tags = 0; - void *tags_handle = NULL; - glite_jp_tagval_t* tags = NULL; - int i,j; - glite_jp_error_t err; - - assert(job != NULL); - assert(attrs_inout != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; attrs_inout[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TIME: -*/ - if (!got_info) { - if (get_job_info(ctx, job, &owner, &tv_reg)) { - err.code = ctx->error->code; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - got_info = 1; - } - break; - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - if (!got_tags) { - if (glite_jppsbe_open_file(ctx, job, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - got_tags = 1; - } - break; -*/ - default: - err.code = EINVAL; - err.desc = "Invalid attribute type"; - goto error_out; - break; - } - - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - attrs_inout[i].value.s = strdup(owner); - if (!attrs_inout[i].value.s) { - err.code = ENOMEM; - err.desc = "Cannot copy owner string"; - goto error_out; - } - break; - case GLITE_JP_ATTR_TIME: - attrs_inout[i].value.time = tv_reg; - break; - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, attrs_inout[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &attrs_inout[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) attrs_inout[i].value.tag.name = NULL; - break; -*/ - default: - break; - } - } - -error_out: - free(owner); - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); - - if (err.code) { - while (i > 0) { - i--; - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - free(attrs_inout[i].value.s); - break; - case GLITE_JP_ATTR_TAG: - free(attrs_inout[i].value.tag.name); - free(attrs_inout[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} -static int compare_timeval(struct timeval a, struct timeval b) -{ - if (a.tv_sec < b.tv_sec) return -1; - if (a.tv_sec > b.tv_sec) return 1; - if (a.tv_usec < b.tv_usec) return -1; - if (a.tv_usec > b.tv_usec) return 1; - return 0; -} - - -/* FIXME: disabled -- clarification wrt. filetype plugin needed */ - -#if 0 - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -); - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - char *time_dirname = NULL; - DIR *time_dirp = NULL; - struct dirent *jobent; - char *info_fname = NULL; - char *jobid = NULL; - char *owner = NULL; - struct timeval tv_reg; - void *tags_handle = NULL; - int matching; - int i, j; - glite_jp_tagval_t* tags = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (asprintf(&time_dirname, "%s/data/%s/%d", config->internal_path, - ownerhash, regtime_tr) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - time_dirp = opendir(time_dirname); - if (!time_dirp) { - free(time_dirname); - return 0; /* found nothing */ - } - while ((jobent = readdir(time_dirp)) != NULL) { - if (!strcmp(jobent->d_name, ".")) continue; - if (!strcmp(jobent->d_name, "..")) continue; - if (asprintf(&info_fname, "%s/%s/_info", time_dirname, - jobent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (get_job_info_int(ctx, info_fname, &jobid, &owner, &tv_reg)) { - err.code = EIO; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - if (q_tags || md_tags) { - if (glite_jppsbe_open_file(ctx, jobid, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - tags_handle = NULL; - } - - matching = 1; - for (i = 0; matching && query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (query[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - if (query[i].value.s == NULL || - strcmp(query[i].value.s, owner)) matching = 0; - break; - case GLITE_JP_ATTR_TIME: - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_LESS: - matching = compare_timeval(tv_reg, query[i].value.time) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = compare_timeval(tv_reg, query[i].value.time) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = compare_timeval(tv_reg, query[i].value.time) >= 0 - && compare_timeval(tv_reg, query[i].value2.time) <= 0; - break; - } - break; - case GLITE_JP_ATTR_TAG: - if (!tags) { - matching = 0; - break; - } - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, query[i].attr.name)) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_LESS: - matching = strcmp(tags[j].value, query[i].value.s) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = strcmp(tags[j].value, query[i].value.s) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = strcmp(tags[j].value, query[i].value.s) >= 0 \ - && strcmp(tags[j].value, query[i].value2.s) <= 0 ; - break; - default: - break; - } - } - } - break; - default: - break; - } - } - if (!matching) { - free(info_fname); info_fname = NULL; - free(jobid); jobid = NULL; - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); tags = NULL; - continue; - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - metadata[i].value.s = owner; - break; - case GLITE_JP_ATTR_TIME: - metadata[i].value.time = tv_reg; - break; - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, metadata[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &metadata[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) { - metadata[i].value.tag.name = NULL; - metadata[i].value.tag.value = NULL; - } - break; - default: - break; - } - } - (*callback)(ctx, jobid, metadata); - free(jobid); jobid = NULL; - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - } - -error_out: - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - if (tags_handle) glite_jppsbe_close_file(ctx, tags_handle); - free(info_fname); - free(owner); - free(jobid); - closedir(time_dirp); - free(time_dirname); - if (err.code) { - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else - return 0; -} - -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - /* XXX clone metadata */ - int i; - char *q_exact_owner = NULL; - char *ownerhash = NULL; - long q_min_time = 0; - long q_max_time = LONG_MAX; - long q_min_time_tr; - long q_max_time_tr; - int q_with_tags = 0; - int md_info = 0; - int md_tags = 0; - char *owner_dirname = NULL; - DIR *owner_dirp = NULL; - struct dirent *ttimeent; - char *data_dirname = NULL; - DIR *data_dirp = NULL; - struct dirent *ownerent; - long ttime = 0; - glite_jp_attrval_t *metadata_templ = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - if (query[i].attr.type == GLITE_JP_ATTR_OWNER && query[i].op == GLITE_JP_QUERYOP_EQUAL) { - q_exact_owner = query[i].value.s; - } - if (query[i].attr.type == GLITE_JP_ATTR_TIME) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - q_min_time = query[i].value.time.tv_sec; - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_LESS: - if (q_max_time > query[i].value.time.tv_sec + 1) - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_WITHIN: - if (q_max_time > query[i].value2.time.tv_sec + 1) - q_max_time = query[i].value2.time.tv_sec + 1; - /* fallthrough */ - case GLITE_JP_QUERYOP_GREATER: - if (q_min_time < query[i].value.time.tv_sec) - q_min_time = query[i].value.time.tv_sec; - break; - default: - err.code = EINVAL; - err.desc = "Invalid query op"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - if (query[i].attr.type == GLITE_JP_ATTR_TAG) - q_with_tags = 1; - - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TIME: - md_info = 1; - break; - case GLITE_JP_ATTR_TAG: - md_tags = 1; - break; - default: - err.code = EINVAL; - err.desc = "Invalid attribute type in metadata parameter"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - metadata_templ = (glite_jp_attrval_t *) calloc(i + 1, sizeof(glite_jp_attrval_t)); - if (!metadata_templ) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - memcpy(metadata_templ, metadata, (i + 1) * sizeof(glite_jp_attrval_t)); - - q_min_time_tr = regtime_trunc(q_min_time); - q_max_time_tr = regtime_ceil(q_max_time); - - if (q_exact_owner) { - ownerhash = str2md5(q_exact_owner); /* static buffer */ - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, ownerhash) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - free(metadata_templ); - return 0; /* found nothing */ - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerhash, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - } else { /* !q_exact_owner */ - if (asprintf(&data_dirname, "%s/data", config->internal_path) == -1) { - err.code = ENOMEM; - goto error_out; - } - data_dirp = opendir(data_dirname); - if (!data_dirp) { - err.code = EIO; - err.desc = "Cannot open data directory"; - goto error_out; - } - while ((ownerent = readdir(data_dirp)) != NULL) { - if (!strcmp(ownerent->d_name, ".")) continue; - if (!strcmp(ownerent->d_name, "..")) continue; - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, - ownerent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - err.code = EIO; - err.desc = "Cannot open owner data directory"; - goto error_out; - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerent->d_name, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - closedir(owner_dirp); owner_dirp = NULL; - } - closedir(data_dirp); data_dirp = NULL; - } - return 0; - -error_out: - if (owner_dirp) closedir(owner_dirp); - if (data_dirp) closedir(data_dirp); - free(data_dirname); - free(metadata_templ); - return glite_jp_stack_error(ctx,&err); -} - -#else - -/* placeholder instead */ -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - glite_jp_error_t err; - err.code = ENOSYS; - err.desc = "not implemented"; - return glite_jp_stack_error(ctx,&err); -} - -#endif - -/* XXX: -- no primary authorization yet -- no concurrency control yet -- partial success in pwrite,append -- "unique" part of jobid is assumed to be unique across bookkeeping servers -- repository versioning not fully implemented yet -*/ diff --git a/org.glite.jp.client/src/is_client.c b/org.glite.jp.client/src/is_client.c deleted file mode 100644 index 8a747ef..0000000 --- a/org.glite.jp.client/src/is_client.c +++ /dev/null @@ -1,38 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" - -#include "feed.h" -/* FIXME -#include "jpis_H.h" -#include "jpis_.nsmap" -*/ - -int glite_jpps_single_feed( - glite_jp_context_t ctx, - const char *destination, - const char *job, - const glite_jp_attrval_t attrs[] -) -{ - /* TODO: really call JP Index server (via interlogger) */ - printf("feed to %s, job %s\n",destination,job); - -/* FIXME */ -#if 0 - if (soap_call_jpsrv__UpdateJobs(ctx->other_soap,destination,"", - /* FIXME: feedId */ "", - /* FIXME: UpdateJobsData */ NULL, - 0, - NULL - )) fprintf(stderr,"UpdateJobs: %s %s\n",ctx->other_soap->fault->faultcode, - ctx->other_soap->fault->faultstring); - -#endif - return 0; -} diff --git a/org.glite.jp.client/src/jpimporter.c b/org.glite.jp.client/src/jpimporter.c deleted file mode 100644 index b54aac8..0000000 --- a/org.glite.jp.client/src/jpimporter.c +++ /dev/null @@ -1,243 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glite/lb/lb_maildir.h" -#include "glite/security/glite_gsplugin.h" - -#include "jpps_H.h" -#include "jpps_.nsmap" - -#include "jptype_map.h" - -#include "soap_version.h" -#if GSOAP_VERSION <= 20602 -#define soap_call___jpsrv__RegisterJob soap_call___ns1__RegisterJob -#endif - - -#ifndef dprintf -#define dprintf(x) { if (debug) printf x; } -#endif - -#ifndef GLITE_JPIMPORTER_PIDFILE -#define GLITE_JPIMPORTER_PIDFILE "/var/run/glite-jpimporter.pid" -#endif - -#ifndef GLITE_JPIMPORTER_MDIR -#define GLITE_JPIMPORTER_MDIR "/tmp/jpreg" -#endif - -static int debug = 0; -static int die = 0; - -static struct option opts[] = { - { "help", 0, NULL, 'h'}, - { "debug", 0, NULL, 'd'}, - { "jpps", 1, NULL, 'p'}, - { "mdir", 1, NULL, 'm'}, - { "pidfile", 1, NULL, 'i'}, - { NULL, 0, NULL, 0} -}; - -static const char *get_opt_string = "hdp:m:i:"; - -static void usage(char *me) -{ - fprintf(stderr,"usage: %s [option]\n" - "\t-h, --help\t displays this screen\n" - "\t-d, --debug\t don't run as daemon, additional diagnostics\n" - "\t-p, --jpps\t JP primary service server\n" - "\t-m, --mdir\t path to the 'LB maildir' subtree\n" - "\t-i, --pidfile\t file to store master pid\n", - me); -} - -static void catchsig(int sig) -{ - die = sig; -} - -int main(int argc, char *argv[]) -{ - struct sigaction sa; - struct soap *soap; - sigset_t sset; - FILE *fpid; - int opt; - char *name, - *jpps = "http://localhost:8901", - pidfile[PATH_MAX] = GLITE_JPIMPORTER_PIDFILE, - mdir[PATH_MAX] = GLITE_JPIMPORTER_MDIR; - - - name = strrchr(argv[0],'/'); - if (name) name++; else name = argv[0]; - - if ( geteuid() ) - snprintf(pidfile, sizeof pidfile, "%s/glite_jpimporter.pid", getenv("HOME")); - - while ( (opt = getopt_long(argc, argv, get_opt_string, opts, NULL)) != EOF ) - switch ( opt ) { - case 'd': debug = 1; break; - case 'h': usage(name); return 0; - case 'p': jpps = optarg; break; - case 'm': strcpy(mdir, optarg); break; - case 'i': strcpy(pidfile, optarg); break; - case '?': usage(name); return 1; - } - if ( optind < argc ) { usage(name); return 1; } - - soap = soap_new(); - soap_init(soap); - soap_set_namespaces(soap, jpps__namespaces); - soap_register_plugin(soap, glite_gsplugin); - - setlinebuf(stdout); - setlinebuf(stderr); - - fpid = fopen(pidfile,"r"); - if ( fpid ) { - int opid = -1; - - if ( fscanf(fpid,"%d",&opid) == 1 ) { - if ( !kill(opid,0) ) { - fprintf(stderr,"%s: another instance running, pid = %d\n",argv[0],opid); - return 1; - } - else if (errno != ESRCH) { perror("kill()"); return 1; } - } - fclose(fpid); - } else if (errno != ENOENT) { perror(pidfile); return 1; } - fpid = fopen(pidfile, "w"); - if ( !fpid ) { perror(pidfile); return 1; } - fprintf(fpid, "%d", getpid()); - fclose(fpid); - - if ( !debug ) { - if ( daemon(1,0) == -1 ) { perror("deamon()"); exit(1); } - - fpid = fopen(pidfile,"w"); - if ( !fpid ) { perror(pidfile); return 1; } - fprintf(fpid, "%d", getpid()); - fclose(fpid); - openlog(name, LOG_PID, LOG_DAEMON); - } else { setpgid(0, getpid()); } - - dprintf(("Master pid %d\n", getpid())); - - memset(&sa, 0, sizeof(sa)); assert(sa.sa_handler == NULL); - sa.sa_handler = catchsig; - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - sa.sa_handler = SIG_IGN; - sigaction(SIGUSR1, &sa, NULL); - - sigemptyset(&sset); - sigaddset(&sset, SIGTERM); - sigaddset(&sset, SIGINT); - sigprocmask(SIG_BLOCK, &sset, NULL); - - while ( !die ) { - int ret; - char *msg = NULL; - char *fname = NULL; - - ret = edg_wll_MaildirTransStart(mdir, &msg, &fname); - /* XXX: where should unblocking signal besides? */ - sigprocmask(SIG_UNBLOCK, &sset, NULL); - sigprocmask(SIG_BLOCK, &sset, NULL); - if ( ret < 0 ) { - dprintf(("edg_wll_MaildirTransStart: %s (%s)\n", strerror(errno), lbm_errdesc)); - if ( !debug ) syslog(LOG_ERR, "edg_wll_MaildirTransStart: %s (%s)", strerror(errno), lbm_errdesc); - exit(1); - } else if ( ret == 0 ) { - sleep(2); - } else { - struct _jpelem__RegisterJob in; - struct _jpelem__RegisterJobResponse empty; - struct SOAP_ENV__Detail *detail; - struct jptype__genericFault *f; - char *aux, *reason, indent[200] = " "; - - - dprintf(("JP registration request received\n")); - if ( !debug ) syslog(LOG_INFO, "JP registration request received\n"); - - if ( !(aux = strchr(msg, '\n')) ) { - dprintf(("Wrong format of message!\n")); - if ( !debug ) syslog(LOG_ERR, "Wrong format of message\n"); - free(msg); - continue; - } - *aux++ = '\0'; - in.job = msg; - in.owner = aux; - ret = soap_call___jpsrv__RegisterJob(soap, jpps, "", &in, &empty); - free(msg); - - switch ( ret ) { - case SOAP_OK: - /* XXX: checks return error code */ - edg_wll_MaildirTransEnd(mdir, fname, LBMD_TRANS_OK); - dprintf(("Job '%s' succesfully registered to JP\n", msg)); - if ( !debug ) syslog(LOG_INFO, "Job '%s' succesfully registered to JP\n", msg); - break; - - case SOAP_FAULT: - case SOAP_SVR_FAULT: - edg_wll_MaildirTransEnd(mdir, fname, LBMD_TRANS_FAILED); - if (soap->version == 2) { - detail = soap->fault->SOAP_ENV__Detail; - reason = soap->fault->SOAP_ENV__Reason; - } else { - detail = soap->fault->detail; - reason = soap->fault->faultstring; - } - dprintf(("%s\n", reason)); - assert(detail->__type == SOAP_TYPE__genericFault); -#if GSOAP_VERSION >=20700 - f = ((struct _genericFault *) detail->fault) -#else - f = ((struct _genericFault *) detail->value) -#endif - -> jpelem__genericFault; - - while ( f ) { - dprintf(("%s%s: %s (%s)\n", indent, f->source, f->text, f->description)); - f = f->reason; - strcat(indent, " "); - } - break; - - default: - soap_print_fault(soap, stderr); - edg_wll_MaildirTransEnd(mdir, fname, LBMD_TRANS_FAILED); - break; - } - free(fname); - } - } - - /* XXX: some sort of soap_destroy(soap) */ - dprintf(("Terminating on signal %d\n", die)); - if ( !debug ) syslog(LOG_INFO, "Terminating on signal %d\n", die); - - unlink(pidfile); - - return 0; -} - -/* XXX: we don't use it */ -SOAP_NMAC struct Namespace namespaces[] = { {NULL,NULL} }; - diff --git a/org.glite.jp.client/src/jptype_map.h b/org.glite.jp.client/src/jptype_map.h deleted file mode 100644 index 56d611f..0000000 --- a/org.glite.jp.client/src/jptype_map.h +++ /dev/null @@ -1,18 +0,0 @@ -#include "soap_version.h" - -#if GSOAP_VERSION >= 20700 -#define INPUT_SANDBOX jptype__UploadClass__INPUT_SANDBOX -#define OUTPUT_SANDBOX jptype__UploadClass__OUTPUT_SANDBOX -#define JOB_LOG jptype__UploadClass__JOB_LOG - -#define OWNER jptype__AttributeType__OWNER -#define TIME jptype__AttributeType__TIME -#define TAG jptype__AttributeType__TAG - -#define EQUAL jptype__queryOp__EQUAL -#define UNEQUAL jptype__queryOp__UNEQUAL -#define LESS jptype__queryOp__LESS -#define GREATER jptype__queryOp__GREATER -#define WITHIN jptype__queryOp__WITHIN -#endif - diff --git a/org.glite.jp.client/src/mysql.c b/org.glite.jp.client/src/mysql.c deleted file mode 100644 index 0f080ce..0000000 --- a/org.glite.jp.client/src/mysql.c +++ /dev/null @@ -1,265 +0,0 @@ -#ident "$Header$" - -#include "mysql.h" // MySql header file -#include "mysqld_error.h" -#include "errmsg.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "db.h" - -#define DEFAULTCS "jpps/@localhost:jpps1" -#define GLITE_JP_LB_MYSQL_VERSION 40018 - -static int my_err(glite_jp_context_t ctx, char *function) -{ - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = function; - err.code = EIO; /* XXX */ - err.desc = mysql_error((MYSQL *) ctx->dbhandle); - return glite_jp_stack_error(ctx,&err); -} - -struct _glite_jp_db_stmt_t { - MYSQL_RES *result; - glite_jp_context_t ctx; -}; - -int glite_jp_db_connect(glite_jp_context_t ctx,char *cs) -{ - char *buf = NULL; - char *host,*user,*pw,*db; - char *slash,*at,*colon; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (!cs) cs = DEFAULTCS; - - if (!(ctx->dbhandle = (void *) mysql_init(NULL))) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - mysql_options(ctx->dbhandle, MYSQL_READ_DEFAULT_FILE, "my"); - - host = user = pw = db = NULL; - - buf = strdup(cs); - slash = strchr(buf,'/'); - at = strrchr(buf,'@'); - colon = strrchr(buf,':'); - - if (!slash || !at || !colon) { - free(buf); - err.code = EINVAL; - err.desc = "Invalid DB connect string"; - return glite_jp_stack_error(ctx,&err); - } - - *slash = *at = *colon = 0; - host = at+1; - user = buf; - pw = slash+1; - db = colon+1; - - if (!mysql_real_connect((MYSQL *) ctx->dbhandle,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) { - free(buf); - return my_err(ctx, __FUNCTION__); - } - - free(buf); - return 0; -} - -void glite_jp_db_close(glite_jp_context_t ctx) -{ - mysql_close((MYSQL *) ctx->dbhandle); - ctx->dbhandle = NULL; -} - -int glite_jp_db_execstmt(glite_jp_context_t ctx,char *txt,glite_jp_db_stmt_t *stmt) -{ - int merr; - int retry_nr = 0; - int do_reconnect = 0; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (stmt) { - *stmt = NULL; - } - - while (retry_nr == 0 || do_reconnect) { - do_reconnect = 0; - if (mysql_query((MYSQL *) ctx->dbhandle,txt)) { - /* error occured */ - switch (merr = mysql_errno((MYSQL *) ctx->dbhandle)) { - case 0: - break; - case ER_DUP_ENTRY: - err.code = EEXIST; - err.desc = mysql_error((MYSQL *) ctx->dbhandle); - glite_jp_stack_error(ctx,&err); - return -1; - break; - case CR_SERVER_LOST: - if (retry_nr <= 0) - do_reconnect = 1; - break; - default: - my_err(ctx, __FUNCTION__); - return -1; - break; - } - } - retry_nr++; - } - - if (stmt) { - *stmt = malloc(sizeof(**stmt)); - if (!*stmt) { - err.code = ENOMEM; - glite_jp_stack_error(ctx,&err); - return -1; - } - memset(*stmt,0,sizeof(**stmt)); - (**stmt).ctx = ctx; - (**stmt).result = mysql_store_result((MYSQL *) ctx->dbhandle); - if (!(**stmt).result) { - if (mysql_errno((MYSQL *) ctx->dbhandle)) { - my_err(ctx, __FUNCTION__); - return -1; - } - } - } else { - MYSQL_RES *r = mysql_store_result((MYSQL *) ctx->dbhandle); - mysql_free_result(r); - } - - return mysql_affected_rows((MYSQL *) ctx->dbhandle); -} - -int glite_jp_db_fetchrow(glite_jp_db_stmt_t stmt,char **res) -{ - MYSQL_ROW row; - glite_jp_context_t ctx = stmt->ctx; - int nr,i; - unsigned long *len; - - glite_jp_clear_error(ctx); - - if (!stmt->result) return 0; - - if (!(row = mysql_fetch_row(stmt->result))) { - if (mysql_errno((MYSQL *) ctx->dbhandle)) { - my_err(ctx, __FUNCTION__); - return -1; - } else return 0; - } - - nr = mysql_num_fields(stmt->result); - len = mysql_fetch_lengths(stmt->result); - for (i=0; iresult))) cols[i++] = f->name; - return i == 0; -} - -void glite_jp_db_freestmt(glite_jp_db_stmt_t *stmt) -{ - if (*stmt) { - if ((**stmt).result) mysql_free_result((**stmt).result); - free(*stmt); - *stmt = NULL; - } -} - - -char *glite_jp_db_timetodb(time_t t) -{ - struct tm *tm = gmtime(&t); - char tbuf[256]; - - /* XXX: the very end of our days */ - if (!tm && t == (time_t) LONG_MAX) return strdup("9999-12-31 23:59:59"); - - sprintf(tbuf,"'%4d-%02d-%02d %02d:%02d:%02d'",tm->tm_year+1900,tm->tm_mon+1, - tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); - - return strdup(tbuf); -} - -time_t glite_jp_db_dbtotime(char *t) -{ - struct tm tm; - - memset(&tm,0,sizeof(tm)); - setenv("TZ","UTC",1); tzset(); - sscanf(t,"%4d-%02d-%02d %02d:%02d:%02d", - &tm.tm_year,&tm.tm_mon,&tm.tm_mday, - &tm.tm_hour,&tm.tm_min,&tm.tm_sec); - tm.tm_year -= 1900; - tm.tm_mon--; - - return mktime(&tm); -} - -int glite_jp_db_dbcheckversion(glite_jp_context_t ctx) -{ - MYSQL *m = (MYSQL *) ctx->dbhandle; - const char *ver_s = mysql_get_server_info(m); - int major,minor,sub,version; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub)) { - err.code = EINVAL; - err.desc = "problem checking MySQL version"; - return glite_jp_stack_error(ctx,&err); - } - - version = 10000*major + 100*minor + sub; - - if (version < GLITE_JP_LB_MYSQL_VERSION) { - char msg[300]; - - snprintf(msg,sizeof msg,"Your MySQL version is %d. At least %d required.",version, GLITE_JP_LB_MYSQL_VERSION); - err.code = EINVAL; - err.desc = msg; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} diff --git a/org.glite.jp.client/src/new_ftp_backend.c b/org.glite.jp.client/src/new_ftp_backend.c deleted file mode 100644 index 930030e..0000000 --- a/org.glite.jp.client/src/new_ftp_backend.c +++ /dev/null @@ -1,1790 +0,0 @@ -#ident "$Header$" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" -#include "glite/jp/strmd5.h" - -#include "tags.h" -#include "backend.h" -#include "db.h" - -#include "jpps_H.h" /* XXX: SOAP_TYPE___jpsrv__GetJob */ - -#define FTPBE_DEFAULT_DB_CS "jpps/@localhost:jpps" - -struct ftpbe_config { - char *internal_path; - char *external_path; - char *db_cs; - char *gridmap; - char *logname; -}; - -static struct ftpbe_config *config = NULL; - -struct fhandle_rec { - int fd; - int fd_append; -}; -typedef struct fhandle_rec *fhandle; - -static struct option ftpbe_opts[] = { - { "ftp-internal-path", 1, NULL, 'I' }, - { "ftp-external-path", 1, NULL, 'E' }, - { "ftp-db-cs", 1, NULL, 'D' }, - { "ftp-gridmap", 1, NULL, 'G' }, - { NULL, 0, NULL, 0 } -}; - -/******************************************************************************* - Internal helpers -*******************************************************************************/ - - -static int config_check( - glite_jp_context_t ctx, - struct ftpbe_config *config) -{ - return config == NULL || - config->internal_path == NULL || - config->external_path == NULL || - config->db_cs == NULL || - config->gridmap == NULL || - config->logname == NULL; - - /* XXX check reality */ -} - -static int jobid_unique_pathname(glite_jp_context_t ctx, const char *job, - char **unique, char **ju_path, int get_path) -{ - char *p; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - p = strrchr(job, '/'); - if (!p) { - err.code = EINVAL; - err.desc = "Malformed jobid"; - return glite_jp_stack_error(ctx,&err); - } - /* XXX thorough checks */ - if (!(*unique = strdup(p+1))) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - if (get_path) { - if (!(*ju_path = strdup(p+1))) { - free(*unique); - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - *(*ju_path + 10) = '\0'; - } - return 0; -} - -static int mkdirpath(const char* path, int prefixlen) -{ - char *wpath, *p; - int goout, ret; - - wpath = strdup(path); - if (!wpath) { - errno = ENOMEM; - return -1; - } - - p = wpath + prefixlen; - goout = 0; - while (!goout) { - while (*p == '/') p++; - while (*p != '/' && *p != '\0') p++; - goout = (*p == '\0'); - *p = '\0'; - ret = mkdir(wpath, S_IRUSR | S_IWUSR | S_IXUSR); - if (ret < 0 && errno != EEXIST) break; - *p = '/'; - } - free(wpath); - return goout ? 0 : ret; -} - -static int store_user(glite_jp_context_t ctx, const char *userid, const char *subj) -{ - glite_jp_error_t err; - char *stmt; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(userid != NULL); - assert(subj != NULL); - - trio_asprintf(&stmt,"insert into users(userid,cert_subj) " - "values ('%|Ss','%|Ss')",userid,subj); - if (!stmt) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - if (ctx->error->code == EEXIST) - glite_jp_clear_error(ctx); - else { - free(stmt); - err.code = EIO; - err.desc = "DB access failed"; - return glite_jp_stack_error(ctx,&err); - } - } - free(stmt); - - return 0; -} - -static long regtime_trunc(long tv_sec) -{ - return tv_sec / (86400*7); -} - -static long regtime_ceil(long tv_sec) -{ - return (tv_sec % (86400*7)) ? tv_sec/(86400*7)+1 : tv_sec/(86400*7) ; -} - -/******************************************************************************** - Backend calls -********************************************************************************/ -int glite_jppsbe_init( - glite_jp_context_t ctx, - int argc, - char *argv[] -) -{ - glite_jp_error_t err; - int opt; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - config = (struct ftpbe_config *) calloc(1, sizeof *config); - if (!config) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - config->logname = getlogin(); - - while ((opt = getopt_long(argc, argv, "I:E:G:", ftpbe_opts, NULL)) != EOF) { - switch (opt) { - case 'I': config->internal_path = optarg; break; - case 'E': config->external_path = optarg; break; - case 'D': config->db_cs = optarg; break; - case 'G': config->gridmap = optarg; break; - default: break; - } - } - - /* Defaults */ - if (!config->db_cs) config->db_cs = strdup(FTPBE_DEFAULT_DB_CS); - - if (config_check(ctx, config)) { - err.code = EINVAL; - err.desc = "Invalid FTP backend configuration"; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jp_db_connect(ctx, config->db_cs)) { - err.code = EIO; - err.desc = "Cannot access backend's database (during init)"; - return glite_jp_stack_error(ctx,&err); - } else { - glite_jp_db_close(ctx); /* slaves open their own connections */ - } - - return 0; -} - -int glite_jppsbe_init_slave( - glite_jp_context_t ctx -) -{ - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (glite_jp_db_connect(ctx, config->db_cs)) { - err.code = EIO; - err.desc = "Cannot access backend's database"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_register_job( - glite_jp_context_t ctx, - const char *job, - const char *owner -) -{ - glite_jp_error_t err; - char *data_dir = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *ownerhash = NULL; - struct timeval reg_tv; - char *stmt = NULL; - char *dbtime = NULL; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job != NULL); - assert(owner != NULL); - - gettimeofday(®_tv, NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - ownerhash = str2md5(owner); /* static buffer */ - if (store_user(ctx, ownerhash, owner)) { - err.code = EIO; - err.desc = "Cannot store user entry"; - goto error_out; - } - - dbtime = glite_jp_db_timetodb(reg_tv.tv_sec); - if (!dbtime) { - err.code = ENOMEM; - goto error_out; - } - - trio_asprintf(&stmt,"insert into jobs(jobid,dg_jobid,owner,reg_time) " - "values ('%|Ss','%|Ss','%|Ss', %s)", - ju, job, ownerhash, dbtime); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - if (ctx->error->code == EEXIST) { - err.code = EEXIST; - err.desc = "Job already registered"; - } - else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - if (asprintf(&data_dir, "%s/data/%s/%d/%s", - config->internal_path, ownerhash, regtime_trunc(reg_tv.tv_sec), ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (mkdirpath(data_dir, strlen(config->internal_path)) < 0 && - errno != EEXIST) { - err.code = errno; - err.desc = "Cannot mkdir jobs's data directory"; - goto error_out; - } - -error_out: - free(data_dir); - free(stmt); free(dbtime); - free(ju); free(ju_path); - - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int add_to_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - gridmap = fopen(config->gridmap, "a"); - if (!gridmap) { - err.code = errno; - err.desc = "Cannot open gridmap file"; - return glite_jp_stack_error(ctx,&err); - } - if (fprintf(gridmap, "\"%s\" %s\n", dn, config->logname) < 6 || - ferror(gridmap)) { - err.code = EIO; - err.desc = "Cannot write to gridmap file"; - fclose(gridmap); - return glite_jp_stack_error(ctx,&err); - } - fclose(gridmap); - return 0; -} - -static int remove_from_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - char *temp_name = NULL; - FILE *temp_file = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - /* XXX */ - return 0; -} - -int glite_jppsbe_start_upload( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - const char *content_type, - char **destination_out, - time_t *commit_before_inout -) -{ - char *data_basename = NULL; - char *data_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *peername = NULL; - char *peerhash = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[2] = { NULL, NULL }; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(destination_out!=NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - peername = glite_jp_peer_name(ctx); - if (peername == NULL) { - err.code = EINVAL; - err.desc = "Cannot obtain client certificate info"; - goto error_out; - } - - trio_asprintf(&stmt, "select owner, reg_time from jobs" - " where jobid='%|Ss'", ju); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such job registered"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 2) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - - glite_jp_db_freestmt(&db_res); - - /* XXX authorization done in soap_ops.c */ - - /* XXX name length */ - if (asprintf(&data_basename, "%s%s%s", class, - (name != NULL) ? "." : "", - (name != NULL) ? name : "") == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(&data_fname, "%s/data/%s/%d/%s/%s", - config->internal_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (asprintf(destination_out, "%s/data/%s/%d/%s/%s", - config->external_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (commit_before_inout != NULL) - /* XXX no timeout enforced */ - /* XXX: gsoap does not like so much, one year should be enough - *commit_before_inout = (time_t) LONG_MAX; - */ - *commit_before_inout = time(NULL) + 365*24*60*60; - - /* - if (add_to_gridmap(ctx, peername)) { - err.code = EIO; - err.desc = "Cannot add peer DN to ftp server authorization file"; - goto error_out; - } - */ - - peerhash = str2md5(peername); /* static buffer */ - if (store_user(ctx, peerhash, peername)) { - err.code = EIO; - err.desc = "Cannot store upload user entry"; - goto error_out; - } - - free(stmt); stmt = NULL; - trio_asprintf(&stmt,"insert into files" - "(jobid,filename,int_path,ext_url,state,deadline,ul_userid) " - "values ('%|Ss','%|Ss','%|Ss','%|Ss','%|Ss', '%|Ss', '%|Ss')", - ju, data_basename, data_fname, *destination_out, "uploading", - glite_jp_db_timetodb(*commit_before_inout), peerhash); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - if (ctx->error->code == EEXIST) { - err.code = EEXIST; - err.desc = "File already stored or upload in progress"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - -error_out: - free(db_row[0]); free(db_row[1]); - free(stmt); - free(data_basename); - free(data_fname); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_commit_upload( - glite_jp_context_t ctx, - const char *destination -) -{ - char *peername = NULL; - char *peerhash = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - int i; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(destination != NULL); - - trio_asprintf(&stmt, "select * from files where " - "ext_url='%|Ss' and state='uploading'", destination); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such upload in progress"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 7) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - glite_jp_db_freestmt(&db_res); - - peername = glite_jp_peer_name(ctx); - if (peername == NULL) { - err.code = EINVAL; - err.desc = "Cannot obtain client certificate info"; - goto error_out; - } - - peerhash = str2md5(peername); /* static buffer */ - if (strcmp(peerhash, db_row[6])) { - err.code = EPERM; - err.desc = "Upload started by client with different identity"; - goto error_out; - } - - free(stmt); - trio_asprintf(&stmt,"update files set state='committed', deadline=NULL " - "where jobid='%|Ss' and filename='%|Ss'", db_row[0], db_row[1]); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } -error_out: - for (i=0; i<7; i++) free(db_row[i]); - free(peername); - free(stmt); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_destination_info( - glite_jp_context_t ctx, - const char *destination, - char **job, - char **class, - char **name -) -{ - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[2] = { NULL, NULL}; - int i; - char *cp = NULL; - - char *classname = NULL; - glite_jp_error_t err; - - assert(destination != NULL); - assert(job != NULL); - assert(class != NULL); - assert(name != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - - trio_asprintf(&stmt, "select j.dg_jobid,f.filename from jobs j,files f where " - "f.ext_url='%|Ss' and j.jobid=f.jobid", destination); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "Invalid destination string"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 2) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - glite_jp_db_freestmt(&db_res); - - *job = strdup(db_row[0]); - - cp = strchr(db_row[1],'.'); - if (!cp) { - *name = NULL; - } else { - *cp++ = '\0'; - *name = strdup(cp); - } - *class = strdup(db_row[1]); - - if (!*job || !*class) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - for (i=0; i<2; i++) free(db_row[i]); - free(stmt); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - - -int glite_jppsbe_get_job_url( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - char **url_out -) -{ - char *data_basename = NULL; - char *data_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[3] = { NULL, NULL, NULL }; - - long reg_time; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(url_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/ : ""name"; - return glite_jp_stack_error(ctx,&err); - } - - trio_asprintf(&stmt, "select j.owner,reg_time,u.cert_subj from jobs j, users u " - "where j.jobid='%|Ss' and j.owner = u.userid", ju); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such job registered"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - free(stmt); stmt = NULL; - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 3) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - - glite_jp_db_freestmt(&db_res); - - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__GetJob,job,db_row[2])) { - err.code = EPERM; - goto error_out; - } - - /* XXX name length */ - if (asprintf(&data_basename, "%s%s%s", class, - (name != NULL) ? "." : "", - (name != NULL) ? name : "") == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(url_out, "%s/data/%s/%d/%s/%s", - config->external_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - - trio_asprintf(&stmt,"select 'x' from files where jobid='%|Ss' " - "and ext_url = '%|Ss' " - "and state='committed' ",ju,*url_out); - - if ((db_retn = glite_jp_db_execstmt(ctx,stmt,&db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "not uploaded yet"; - } - else { - err.code = EIO; - err.desc = "DB access failed"; - } - /* goto error_out; */ - } - -error_out: - free(db_row[0]); free(db_row[1]); - free(stmt); - free(data_basename); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_fname( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - char **fname_out -) -{ - char *data_basename = NULL; - char *ju = NULL; - char *ju_path = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[2] = { NULL, NULL }; - - long reg_time; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(fname_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - trio_asprintf(&stmt, "select owner, reg_time from jobs " - "where jobid='%|Ss'", ju); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such job registered"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 2) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - - glite_jp_db_freestmt(&db_res); - - /* XXX name length */ - if (asprintf(&data_basename, "%s%s%s", class, - (name != NULL) ? "." : "", (name != NULL) ? name : "") == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(fname_out, "%s/data/%s/%d/%s/%s", - config->internal_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - free(db_row[0]); free(db_row[1]); - free(stmt); - free(data_basename); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - - -int glite_jppsbe_open_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - int mode, - void **handle_out -) -{ - fhandle handle = NULL; - char* fname = NULL; - glite_jp_error_t err; - - assert(handle_out != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (get_job_fname(ctx, job, class, name, &fname)) { - err.code = ctx->error->code; - err.desc = "Cannot construct internal filename"; - return glite_jp_stack_error(ctx,&err); - } - - handle = (fhandle) calloc(1,sizeof(*handle)); - if (handle == NULL) { - err.code = ENOMEM; - goto error_out; - } - - handle->fd = open(fname, mode, S_IRUSR | S_IWUSR); - if (handle->fd < 0) { - err.code = errno; - err.desc = "Cannot open requested file"; - free(handle); - goto error_out; - } - handle->fd_append = open(fname, mode | O_APPEND, S_IRUSR | S_IWUSR); - if (handle->fd_append < 0) { - err.code = errno; - err.desc = "Cannot open requested file for append"; - close(handle->fd); - free(handle); - goto error_out; - } - *handle_out = (void*) handle; - -error_out: - free(fname); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_close_file( - glite_jp_context_t ctx, - void *handle -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (close(((fhandle)handle)->fd_append) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor (fd_append)"; - goto error_out; - } - if (close(((fhandle)handle)->fd) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor"; - goto error_out; - } - -error_out: - free(handle); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_pread( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset, - ssize_t *nbytes_ret -) -{ - ssize_t ret; - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if ((ret = pread(((fhandle)handle)->fd, buf, nbytes, offset)) < 0) { - err.code = errno; - err.desc = "Error in pread()"; - return glite_jp_stack_error(ctx,&err); - } - *nbytes_ret = ret; - - return 0; -} - -int glite_jppsbe_pwrite( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (pwrite(((fhandle)handle)->fd, buf, nbytes, offset) < 0) { - err.code = errno; - err.desc = "Error in pwrite()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_append( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (write(((fhandle)handle)->fd_append, buf, nbytes) < 0) { - err.code = errno; - err.desc = "Error in write()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -static int get_job_info( - glite_jp_context_t ctx, - const char *job, - char **owner, - struct timeval *tv_reg -) -{ - char *ju = NULL; - char *ju_path = NULL; - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char *int_fname = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %*s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, &ownerlen) < 4 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - free(int_fname); - free(ju); - free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_info_int( - glite_jp_context_t ctx, - const char *int_fname, - char **jobid, - char **owner, - struct timeval *tv_reg -) -{ - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char jobid_buf[256]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, jobid_buf, &ownerlen) < 5 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - *jobid = strdup(jobid_buf); - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_get_job_metadata( - glite_jp_context_t ctx, - const char *job, - glite_jp_attrval_t attrs_inout[] -) -{ - int got_info = 0; - struct timeval tv_reg; - char *owner = NULL; - int got_tags = 0; - void *tags_handle = NULL; - glite_jp_tagval_t* tags = NULL; - int i,j; - glite_jp_error_t err; - - assert(job != NULL); - assert(attrs_inout != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; attrs_inout[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TIME: -*/ - if (!got_info) { - if (get_job_info(ctx, job, &owner, &tv_reg)) { - err.code = ctx->error->code; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - got_info = 1; - } - break; - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - if (!got_tags) { - if (glite_jppsbe_open_file(ctx, job, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - got_tags = 1; - } - break; -*/ - default: - err.code = EINVAL; - err.desc = "Invalid attribute type"; - goto error_out; - break; - } - - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - attrs_inout[i].value.s = strdup(owner); - if (!attrs_inout[i].value.s) { - err.code = ENOMEM; - err.desc = "Cannot copy owner string"; - goto error_out; - } - break; - case GLITE_JP_ATTR_TIME: - attrs_inout[i].value.time = tv_reg; - break; - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, attrs_inout[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &attrs_inout[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) attrs_inout[i].value.tag.name = NULL; - break; -*/ - default: - break; - } - } - -error_out: - free(owner); - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); - - if (err.code) { - while (i > 0) { - i--; - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - free(attrs_inout[i].value.s); - break; - case GLITE_JP_ATTR_TAG: - free(attrs_inout[i].value.tag.name); - free(attrs_inout[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} -static int compare_timeval(struct timeval a, struct timeval b) -{ - if (a.tv_sec < b.tv_sec) return -1; - if (a.tv_sec > b.tv_sec) return 1; - if (a.tv_usec < b.tv_usec) return -1; - if (a.tv_usec > b.tv_usec) return 1; - return 0; -} - - -/* FIXME: disabled -- clarification wrt. filetype plugin needed */ - -#if 0 - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -); - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - char *time_dirname = NULL; - DIR *time_dirp = NULL; - struct dirent *jobent; - char *info_fname = NULL; - char *jobid = NULL; - char *owner = NULL; - struct timeval tv_reg; - void *tags_handle = NULL; - int matching; - int i, j; - glite_jp_tagval_t* tags = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (asprintf(&time_dirname, "%s/data/%s/%d", config->internal_path, - ownerhash, regtime_tr) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - time_dirp = opendir(time_dirname); - if (!time_dirp) { - free(time_dirname); - return 0; /* found nothing */ - } - while ((jobent = readdir(time_dirp)) != NULL) { - if (!strcmp(jobent->d_name, ".")) continue; - if (!strcmp(jobent->d_name, "..")) continue; - if (asprintf(&info_fname, "%s/%s/_info", time_dirname, - jobent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (get_job_info_int(ctx, info_fname, &jobid, &owner, &tv_reg)) { - err.code = EIO; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - if (q_tags || md_tags) { - if (glite_jppsbe_open_file(ctx, jobid, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - tags_handle = NULL; - } - - matching = 1; - for (i = 0; matching && query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (query[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - if (query[i].value.s == NULL || - strcmp(query[i].value.s, owner)) matching = 0; - break; - case GLITE_JP_ATTR_TIME: - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_LESS: - matching = compare_timeval(tv_reg, query[i].value.time) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = compare_timeval(tv_reg, query[i].value.time) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = compare_timeval(tv_reg, query[i].value.time) >= 0 - && compare_timeval(tv_reg, query[i].value2.time) <= 0; - break; - } - break; - case GLITE_JP_ATTR_TAG: - if (!tags) { - matching = 0; - break; - } - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, query[i].attr.name)) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_LESS: - matching = strcmp(tags[j].value, query[i].value.s) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = strcmp(tags[j].value, query[i].value.s) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = strcmp(tags[j].value, query[i].value.s) >= 0 \ - && strcmp(tags[j].value, query[i].value2.s) <= 0 ; - break; - default: - break; - } - } - } - break; - default: - break; - } - } - if (!matching) { - free(info_fname); info_fname = NULL; - free(jobid); jobid = NULL; - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); tags = NULL; - continue; - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - metadata[i].value.s = owner; - break; - case GLITE_JP_ATTR_TIME: - metadata[i].value.time = tv_reg; - break; - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, metadata[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &metadata[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) { - metadata[i].value.tag.name = NULL; - metadata[i].value.tag.value = NULL; - } - break; - default: - break; - } - } - (*callback)(ctx, jobid, metadata); - free(jobid); jobid = NULL; - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - } - -error_out: - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - if (tags_handle) glite_jppsbe_close_file(ctx, tags_handle); - free(info_fname); - free(owner); - free(jobid); - closedir(time_dirp); - free(time_dirname); - if (err.code) { - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else - return 0; -} - -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - /* XXX clone metadata */ - int i; - char *q_exact_owner = NULL; - char *ownerhash = NULL; - long q_min_time = 0; - long q_max_time = LONG_MAX; - long q_min_time_tr; - long q_max_time_tr; - int q_with_tags = 0; - int md_info = 0; - int md_tags = 0; - char *owner_dirname = NULL; - DIR *owner_dirp = NULL; - struct dirent *ttimeent; - char *data_dirname = NULL; - DIR *data_dirp = NULL; - struct dirent *ownerent; - long ttime = 0; - glite_jp_attrval_t *metadata_templ = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - if (query[i].attr.type == GLITE_JP_ATTR_OWNER && query[i].op == GLITE_JP_QUERYOP_EQUAL) { - q_exact_owner = query[i].value.s; - } - if (query[i].attr.type == GLITE_JP_ATTR_TIME) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - q_min_time = query[i].value.time.tv_sec; - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_LESS: - if (q_max_time > query[i].value.time.tv_sec + 1) - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_WITHIN: - if (q_max_time > query[i].value2.time.tv_sec + 1) - q_max_time = query[i].value2.time.tv_sec + 1; - /* fallthrough */ - case GLITE_JP_QUERYOP_GREATER: - if (q_min_time < query[i].value.time.tv_sec) - q_min_time = query[i].value.time.tv_sec; - break; - default: - err.code = EINVAL; - err.desc = "Invalid query op"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - if (query[i].attr.type == GLITE_JP_ATTR_TAG) - q_with_tags = 1; - - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TIME: - md_info = 1; - break; - case GLITE_JP_ATTR_TAG: - md_tags = 1; - break; - default: - err.code = EINVAL; - err.desc = "Invalid attribute type in metadata parameter"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - metadata_templ = (glite_jp_attrval_t *) calloc(i + 1, sizeof(glite_jp_attrval_t)); - if (!metadata_templ) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - memcpy(metadata_templ, metadata, (i + 1) * sizeof(glite_jp_attrval_t)); - - q_min_time_tr = regtime_trunc(q_min_time); - q_max_time_tr = regtime_ceil(q_max_time); - - if (q_exact_owner) { - ownerhash = str2md5(q_exact_owner); /* static buffer */ - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, ownerhash) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - free(metadata_templ); - return 0; /* found nothing */ - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerhash, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - } else { /* !q_exact_owner */ - if (asprintf(&data_dirname, "%s/data", config->internal_path) == -1) { - err.code = ENOMEM; - goto error_out; - } - data_dirp = opendir(data_dirname); - if (!data_dirp) { - err.code = EIO; - err.desc = "Cannot open data directory"; - goto error_out; - } - while ((ownerent = readdir(data_dirp)) != NULL) { - if (!strcmp(ownerent->d_name, ".")) continue; - if (!strcmp(ownerent->d_name, "..")) continue; - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, - ownerent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - err.code = EIO; - err.desc = "Cannot open owner data directory"; - goto error_out; - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerent->d_name, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - closedir(owner_dirp); owner_dirp = NULL; - } - closedir(data_dirp); data_dirp = NULL; - } - return 0; - -error_out: - if (owner_dirp) closedir(owner_dirp); - if (data_dirp) closedir(data_dirp); - free(data_dirname); - free(metadata_templ); - return glite_jp_stack_error(ctx,&err); -} - -#else - -/* placeholder instead */ -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - glite_jp_error_t err; - err.code = ENOSYS; - err.desc = "not implemented"; - return glite_jp_stack_error(ctx,&err); -} - -#endif - -/* XXX: -- no primary authorization yet -- no concurrency control yet -- partial success in pwrite,append -- "unique" part of jobid is assumed to be unique across bookkeeping servers -- repository versioning not fully implemented yet -*/ diff --git a/org.glite.jp.client/src/simple_server.c b/org.glite.jp.client/src/simple_server.c deleted file mode 100644 index 3bbb743..0000000 --- a/org.glite.jp.client/src/simple_server.c +++ /dev/null @@ -1,59 +0,0 @@ -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "jpps_H.h" - -extern SOAP_NMAC struct Namespace jpis__namespaces[],jpps__namespaces[]; - -int main(int argc, char *argv[]) { - struct soap soap; - int i, m, s; // master and slave sockets - - glite_jp_context_t ctx; - - soap_init(&soap); - soap_set_namespaces(&soap, jpps__namespaces); - - glite_jp_init_context(&ctx); - - if (glite_jppsbe_init(ctx, &argc, argv)) { - /* XXX log */ - fputs(glite_jp_error_chain(ctx), stderr); - exit(1); - } - - soap.user = (void *) ctx; - - ctx->other_soap = soap_new(); - soap_init(ctx->other_soap); - soap_set_namespaces(ctx->other_soap,jpis__namespaces); - - srand48(time(NULL)); /* feed id generation */ - - m = soap_bind(&soap, NULL, 8901, 100); - if (m < 0) - soap_print_fault(&soap, stderr); - else - { - fprintf(stderr, "Socket connection successful: master socket = %d\n", m); - for (i = 1; ; i++) { - s = soap_accept(&soap); - if (s < 0) { - soap_print_fault(&soap, stderr); - break; - } - jpps__serve(&soap); // process RPC request - soap_destroy(&soap); // clean up class instances - soap_end(&soap); // clean up everything and close socket - glite_jp_run_deferred(ctx); - } - } - soap_done(&soap); // close master socket - - return 0; -} - -/* XXX: we don't use it */ -SOAP_NMAC struct Namespace namespaces[] = { {NULL,NULL} }; diff --git a/org.glite.jp.client/src/soap_ops.c b/org.glite.jp.client/src/soap_ops.c deleted file mode 100644 index 9411403..0000000 --- a/org.glite.jp.client/src/soap_ops.c +++ /dev/null @@ -1,465 +0,0 @@ -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "feed.h" - -#include "jpps_H.h" -/* #include "JobProvenancePS.nsmap" */ -#include "jpps_.nsmap" - -#include "jptype_map.h" - -#include "file_plugin.h" -#include "builtin_plugins.h" - -#include "soap_version.h" -#if GSOAP_VERSION <= 20602 -#define __jpsrv__RegisterJob __ns1__RegisterJob -#define __jpsrv__StartUpload __ns1__StartUpload -#define __jpsrv__CommitUpload __ns1__CommitUpload -#define __jpsrv__RecordTag __ns1__RecordTag -#define __jpsrv__FeedIndex __ns1__FeedIndex -#define __jpsrv__FeedIndexRefresh __ns1__FeedIndexRefresh -#define __jpsrv__GetJob __ns1__GetJob -#endif - -static struct jptype__genericFault *jp2s_error(struct soap *soap, - const glite_jp_error_t *err) -{ - struct jptype__genericFault *ret = NULL; - if (err) { - ret = soap_malloc(soap,sizeof *ret); - memset(ret,0,sizeof *ret); - ret->code = err->code; - ret->source = soap_strdup(soap,err->source); - ret->text = soap_strdup(soap,strerror(err->code)); - ret->description = soap_strdup(soap,err->desc); - ret->reason = jp2s_error(soap,err->reason); - } - return ret; -} - -static void err2fault(const glite_jp_context_t ctx,struct soap *soap) -{ - char *et; - struct SOAP_ENV__Detail *detail = soap_malloc(soap,sizeof *detail); - struct _genericFault *f = soap_malloc(soap,sizeof *f); - - - f->jpelem__genericFault = jp2s_error(soap,ctx->error); - - detail->__type = SOAP_TYPE__genericFault; -#if GSOAP_VERSION >= 20700 - detail->fault = f; -#else - detail->value = f; -#endif - detail->__any = NULL; - - soap_receiver_fault(soap,"Oh, shit!",NULL); - if (soap->version == 2) soap->fault->SOAP_ENV__Detail = detail; - else soap->fault->detail = detail; -} - -/* deprecated -static glite_jp_fileclass_t s2jp_fileclass(enum jptype__UploadClass class) -{ - switch (class) { - case INPUT_SANDBOX: return GLITE_JP_FILECLASS_INPUT; - case OUTPUT_SANDBOX: return GLITE_JP_FILECLASS_OUTPUT; - case JOB_LOG: return GLITE_JP_FILECLASS_LBLOG; - default: return GLITE_JP_FILECLASS_UNDEF; - } -} -*/ - -static void s2jp_tag(const struct jptype__tagValue *stag,glite_jp_tagval_t *jptag) -{ - memset(jptag,0,sizeof *jptag); - jptag->name = strdup(stag->name); - jptag->sequence = stag->sequence ? *stag->sequence : 0; - jptag->timestamp = stag->timestamp ? *stag->timestamp : 0; - if (stag->stringValue) jptag->value = strdup(stag->stringValue); - else if (stag->blobValue) { - jptag->binary = 1; - jptag->size = stag->blobValue->__size; - jptag->value = (char *) stag->blobValue->__ptr; - } -} - -#define CONTEXT_FROM_SOAP(soap,ctx) glite_jp_context_t ctx = (glite_jp_context_t) ((soap)->user) - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__RegisterJob( - struct soap *soap, - struct _jpelem__RegisterJob *in, - struct _jpelem__RegisterJobResponse *empty) -{ - CONTEXT_FROM_SOAP(soap,ctx); - glite_jp_attrval_t owner_val[2]; - - printf("%s %s %s\n",__FUNCTION__,in->job,in->owner); - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__RegisterJob,in->job,in->owner) || - glite_jppsbe_register_job(ctx,in->job,in->owner)) - { - err2fault(ctx,soap); - return SOAP_FAULT; - } - - owner_val[0].attr.type = GLITE_JP_ATTR_OWNER; - owner_val[0].value.s = in->owner; - owner_val[1].attr.type = GLITE_JP_ATTR_UNDEF; - -/* XXX: errrors should be ingored but not silently */ - glite_jpps_match_attr(ctx,in->job,owner_val); - - return SOAP_OK; -} - - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__StartUpload( - struct soap *soap, - struct _jpelem__StartUpload *in, - struct _jpelem__StartUploadResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - char *destination; - time_t commit_before = in->commitBefore; - glite_jp_error_t err; - glite_jpps_fplug_data_t **pd = NULL; - int i; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__StartUpload,NULL,NULL)) { - err2fault(ctx,soap); - return SOAP_FAULT; - } - - switch (glite_jpps_fplug_lookup(ctx,in->class_,&pd)) { - case ENOENT: - err.code = ENOENT; - err.source = __FUNCTION__; - err.desc = "unknown file class"; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - return SOAP_FAULT; - case 0: break; - default: - err2fault(ctx,soap); - return SOAP_FAULT; - } - - for (i=0; pd[0]->uris[i] && strcmp(pd[0]->uris[i],in->class_); i++); - assert(pd[0]->uris[i]); - - if (glite_jppsbe_start_upload(ctx,in->job,pd[0]->classes[i],in->name,in->contentType, - &destination,&commit_before)) - { - err2fault(ctx,soap); - free(pd); - return SOAP_FAULT; - } - - out->destination = soap_strdup(soap,destination); - free(destination); - out->commitBefore = commit_before; - - free(pd); - return SOAP_OK; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__CommitUpload( - struct soap *soap, - struct _jpelem__CommitUpload *in, - struct _jpelem__CommitUploadResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - char *job,*class,*name; - - job = class = name = NULL; - - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__CommitUpload,NULL,NULL) || - glite_jppsbe_commit_upload(ctx,in->destination)) - { - err2fault(ctx,soap); - return SOAP_FAULT; - } - - /* XXX: should not fail when commit_upload was OK */ - assert(glite_jppsbe_destination_info(ctx,in->destination,&job,&class,&name) == 0); - - /* XXX: ignore errors but don't fail silenty */ - glite_jpps_match_file(ctx,job,class,name); - - free(job); free(class); free(name); - - return SOAP_OK; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__RecordTag( - struct soap *soap, - struct _jpelem__RecordTag *in, - struct _jpelem__RecordTagResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - void *file_be,*file_p; - glite_jpps_fplug_data_t **pd = NULL; - - glite_jp_tagval_t mytag; - - file_be = file_p = NULL; - - /* XXX: we assume just one plugin and also that TAGS plugin handles - * just one uri/class */ - - if (glite_jpps_fplug_lookup(ctx,GLITE_JP_FILETYPE_TAGS,&pd) - || glite_jppsbe_open_file(ctx,in->jobid,pd[0]->classes[0],NULL, - O_RDWR|O_CREAT,&file_be) - /* XXX: tags need reading to check magic number */ - ) { - free(pd); - err2fault(ctx,soap); - return SOAP_FAULT; - } - - s2jp_tag(in->tag,&mytag); - - /* XXX: assuming tag plugin handles just one type */ - if (pd[0]->ops.open(pd[0]->fpctx,file_be,GLITE_JP_FILETYPE_TAGS,&file_p) - || pd[0]->ops.generic(pd[0]->fpctx,file_p,GLITE_JP_FPLUG_TAGS_APPEND,&mytag)) - { - err2fault(ctx,soap); - if (file_p) pd[0]->ops.close(pd[0]->fpctx,file_p); - glite_jppsbe_close_file(ctx,file_be); - free(pd); - return SOAP_FAULT; - } - - if (pd[0]->ops.close(pd[0]->fpctx,file_p) - || glite_jppsbe_close_file(ctx,file_be)) - { - err2fault(ctx,soap); - free(pd); - return SOAP_FAULT; - } - - /* XXX: ignore errors but don't fail silenty */ - glite_jpps_match_tag(ctx,in->jobid,&mytag); - - free(pd); - return SOAP_OK; -} - -extern char *glite_jp_default_namespace; - -/* XXX: should be public */ -#define GLITE_JP_TAGS_NAMESPACE "http://glite.org/services/jp/tags" - -static void s2jp_attr(const char *in,glite_jp_attr_t *out) -{ - char *buf = strdup(in),*name = strchr(buf,':'),*ns = NULL; - - if (name) { - ns = buf; - *name++ = 0; - } - else { - name = buf; - ns = glite_jp_default_namespace; - } - - memset(out,0,sizeof *out); - - if (strcmp(ns,glite_jp_default_namespace)) - out->type = strcmp(ns,GLITE_JP_TAGS_NAMESPACE) ? - GLITE_JP_ATTR_GENERIC : GLITE_JP_ATTR_TAG; - else { - if (!strcmp(name,"owner")) out->type = GLITE_JP_ATTR_OWNER; - else if (!strcmp(name,"time")) out->type = GLITE_JP_ATTR_OWNER; - - } - - if (out->type) { - out->name = strdup(name); - out->namespace = strdup(ns); - } -} - -static void s2jp_queryval( - const char *in, - glite_jp_attrtype_t type, - union _glite_jp_query_rec_val *out) -{ - switch (type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TAG: - case GLITE_JP_ATTR_GENERIC: - out->s = strdup(in); - break; - case GLITE_JP_ATTR_TIME: - out->time.tv_sec = atoi(in); - break; - } -} - -static void s2jp_query(const struct jptype__primaryQuery *in, glite_jp_query_rec_t *out) -{ - s2jp_attr(in->attr,&out->attr); - - switch (in->op) { - case EQUAL: out->op = GLITE_JP_QUERYOP_EQUAL; break; - case UNEQUAL: out->op = GLITE_JP_QUERYOP_UNEQUAL; break; - case LESS: out->op = GLITE_JP_QUERYOP_LESS; break; - case GREATER: out->op = GLITE_JP_QUERYOP_GREATER; break; - case WITHIN: - out->op = GLITE_JP_QUERYOP_WITHIN; - s2jp_queryval(in->value2,out->attr.type,&out->value2); - break; - } - - s2jp_queryval(in->value,out->attr.type,&out->value); -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__FeedIndex( - struct soap *soap, - struct _jpelem__FeedIndex *in, - struct _jpelem__FeedIndexResponse *out) -{ - -/* deferred processing: return feed_id to the index server first, - * start feeding it afterwards -- not before the index server actually - * knows feed_id and is ready to accept the feed. - * - * Has to be done within the same server slave, - * passed through the context */ - - CONTEXT_FROM_SOAP(soap,ctx); - char *feed_id = NULL; - time_t expires = 0; - int ret = SOAP_OK; - - glite_jp_attr_t *attrs = calloc(in->__sizeattributes+1,sizeof *attrs); - glite_jp_query_rec_t *qry = calloc(in->__sizeconditions+1,sizeof *qry); - int i; - - glite_jp_clear_error(ctx); - - for (i = 0; i__sizeattributes; i++) s2jp_attr(in->attributes[i],attrs+i); - for (i = 0; i__sizeconditions; i++) s2jp_query(in->conditions[i],qry+i); - - if (in->history) { - if (glite_jpps_run_feed(ctx,in->destination,attrs,qry,&feed_id)) { - err2fault(ctx,soap); - ret = SOAP_FAULT; - goto cleanup; - } - } - - if (in->continuous) { - if (glite_jpps_register_feed(ctx,in->destination,attrs,qry,&feed_id,&expires)) { - err2fault(ctx,soap); - ret = SOAP_FAULT; - goto cleanup; - } - } - - if (!in->history && !in->continuous) { - glite_jp_error_t err; - memset(&err,0,sizeof err); - err.code = EINVAL; - err.source = __FUNCTION__; - err.desc = "at least one of and must be true"; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - ret = SOAP_FAULT; - goto cleanup; - } - - out->feedExpires = expires; - out->feedId = soap_strdup(soap,feed_id); - -cleanup: - free(feed_id); - for (i=0; attrs[i].type; i++) free(attrs[i].name); - free(attrs); - for (i=0; qry[i].attr.type; i++) glite_jp_free_query_rec(qry+i); - free(qry); - - return ret; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__FeedIndexRefresh( - struct soap *soap, - struct _jpelem__FeedIndexRefresh *in, - struct _jpelem__FeedIndexRefreshResponse *out) -{ - fprintf(stderr,"%s: not implemented\n",__FUNCTION__); - abort(); -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__GetJob( - struct soap *soap, - struct _jpelem__GetJob *in, - struct _jpelem__GetJobResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - char *url; - - int i,n; - glite_jp_error_t err; - void **pd; - struct jptype__jppsFile **f = NULL; - - memset(&err,0,sizeof err); - out->__sizefiles = 0; - - for (pd = ctx->plugins; *pd; pd++) { - glite_jpps_fplug_data_t *plugin = *pd; - - for (i=0; plugin->uris[i]; i++) { - glite_jp_clear_error(ctx); - switch (glite_jppsbe_get_job_url(ctx,in->jobid,plugin->classes[i],NULL,&url)) { - case 0: n = out->__sizefiles++; - f = realloc(f,out->__sizefiles * sizeof *f); - f[n] = soap_malloc(soap, sizeof **f); - f[n]->class_ = soap_strdup(soap,plugin->uris[i]); - f[n]->name = NULL; - f[n]->url = soap_strdup(soap,url); - free(url); - break; - case ENOENT: - break; - default: - err.code = ctx->error->code; - err.source = "jpsrv__GetJob()"; - err.desc = plugin->uris[i]; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - glite_jp_clear_error(ctx); - return SOAP_FAULT; - } - } - } - - if (!out->__sizefiles) { - glite_jp_clear_error(ctx); - err.code = ENOENT; - err.source = __FUNCTION__; - err.desc = "No file found for this job"; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - glite_jp_clear_error(ctx); - return SOAP_FAULT; - } - - out->files = soap_malloc(soap,out->__sizefiles * sizeof *f); - memcpy(out->files,f,out->__sizefiles * sizeof *f); - - return SOAP_OK; -} - diff --git a/org.glite.jp.client/src/tags.c b/org.glite.jp.client/src/tags.c deleted file mode 100644 index 1f11b4d..0000000 --- a/org.glite.jp.client/src/tags.c +++ /dev/null @@ -1,233 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include "tags.h" -#include "backend.h" - -/* magic name_len value_len binary sequence timestamp */ -#define HEADER "JP#TAG# %05u %012lu %c %05u %012lu#" -#define HEADER_SIZE 48 - -int glite_jpps_tag_append( - glite_jp_context_t ctx, - void *handle, - const glite_jp_tagval_t *tag -) -{ - char hdr[HEADER_SIZE+1]; - glite_jp_error_t err; - - unsigned long vlen = tag->binary ? tag->size : - (tag->value ? strlen(tag->value) : 0); - int nlen; - - memset(&err,0,sizeof err); - err.source = "glite_jpps_tag_append()"; - - if (!tag->name) { - err.code = EINVAL; - err.desc = "tag name"; - return glite_jp_stack_error(ctx,&err); - } - - nlen = strlen(tag->name); - - assert(sprintf(hdr,HEADER,nlen,vlen, - tag->binary ? "B" : "S", - tag->sequence, tag->timestamp) == HEADER_SIZE); - - if (glite_jppsbe_append(ctx,handle,hdr,HEADER_SIZE)) { - err.code = EIO; - err.desc = "write tag header"; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jppsbe_append(ctx,handle,tag->name,nlen)) { - err.code = EIO; - err.desc = "write tag name"; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jppsbe_append(ctx,handle,tag->value,vlen)) { - err.code = EIO; - err.desc = "write tag value"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jpps_tagval_copy( - glite_jp_context_t ctx, - glite_jp_tagval_t *from, - glite_jp_tagval_t *to -) -{ - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - to->name = strdup(from->name); - if (!to->name) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - to->sequence = from->sequence; - to->timestamp = from->timestamp; - to->binary = from->binary; - to->size = from->size; - to->value = (char *) malloc(to->size); - if (!to->value) { - free(to->name); - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - memcpy(from->value, to->value, to->size); - - return 0; -} - -int glite_jpps_tag_read( - glite_jp_context_t ctx, - void *handle, - off_t offset, - glite_jp_tagval_t *tagvalue, - size_t *shift -) -{ - char hdr[HEADER_SIZE+1]; - unsigned int nlen; - unsigned long vlen; - char binary; - unsigned sequence; - unsigned timestamp; - char * name = NULL; - char * value = NULL; - ssize_t ret; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - hdr[HEADER_SIZE] = '\0'; - if (glite_jppsbe_pread(ctx, handle, hdr, HEADER_SIZE, offset, &ret)) { - err.code = EIO; - err.desc = "Cannot read tag header"; - goto error_out; - } - if (ret == 0) { - err.code = ENOENT; - err.desc = "No more tags in the file"; - goto error_out; - } - /* #define HEADER "JP#TAG# %05u %012lu %c %05u %012lu#" */ - if (sscanf(hdr, HEADER, &nlen, &vlen, &binary, &sequence, ×tamp) < 5) { - err.code = EILSEQ; - err.desc = "Incorrect tag header format"; - goto error_out; - } - name = (char*) malloc(nlen + 1); - if (!name) { - err.code = ENOMEM; - goto error_out; - } - name[nlen] = '\0'; - value = (char*) malloc(vlen + 1); - if (!value) { - err.code = ENOMEM; - goto error_out; - } - value[vlen] = '\0'; - if (glite_jppsbe_pread(ctx, handle, name, nlen, offset + HEADER_SIZE, &ret)) { - err.code = EIO; - err.desc = "Cannot read tag name"; - goto error_out; - } - if (glite_jppsbe_pread(ctx, handle, value, vlen, offset + HEADER_SIZE + nlen, &ret)) { - err.code = EIO; - err.desc = "Cannot read tag value"; - goto error_out; - } - - tagvalue->name = name; - tagvalue->sequence = sequence; - tagvalue->timestamp = timestamp; - tagvalue->binary = (binary == 'B') ? 1 : 0; - tagvalue->size = vlen; - tagvalue->value = value; - - *shift = HEADER_SIZE + nlen + vlen; - - return 0; -error_out: - free(name); - free(value); - return glite_jp_stack_error(ctx,&err); -} - -/* -int glite_jpps_tag_read(glite_jp_context_t, void *, off_t, glite_jp_tagval_t *, size_t); -int glite_jpps_tag_readall(glite_jp_context_t, void *, glite_jp_tagval_t **); -*/ - -int glite_jpps_tag_readall( - glite_jp_context_t ctx, - void *handle, - glite_jp_tagval_t **tags_out -) -{ - glite_jp_tagval_t * tags = NULL; - void * newspace; - int ntags = 0; - int ntagspace = 0; - off_t offset = 0; - int ret; - size_t shift; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - ntagspace = 1; - tags = (glite_jp_tagval_t *) calloc(ntagspace + 1, sizeof(*tags)); - if (!tags) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - while (!(ret = glite_jpps_tag_read(ctx, handle, offset, &tags[ntags], &shift))) { - offset += shift; - ntags++; - if (ntagspace <= ntags) { - ntagspace += 1; - newspace = realloc(tags, (ntagspace + 1) * sizeof(*tags)); - if (!newspace) { - err.code = ENOMEM; - goto error_out; - } - tags = (glite_jp_tagval_t *) newspace; - } - } - if (ret == ENOENT) { - *tags_out = tags; - return 0; - } else { - err.code = EIO; - err.desc = "Error reading tag value"; - } - -error_out: - for (; ntags-- ;) { - free(tags[ntags].name); - free(tags[ntags].value); - } - free(tags); - return glite_jp_stack_error(ctx,&err); -} diff --git a/org.glite.jp.client/src/tags.h b/org.glite.jp.client/src/tags.h deleted file mode 100644 index 0d8afa8..0000000 --- a/org.glite.jp.client/src/tags.h +++ /dev/null @@ -1 +0,0 @@ -int glite_jpps_tag_append(glite_jp_context_t,void *,const glite_jp_tagval_t *); diff --git a/org.glite.jp.client/src/tags_plugin.c b/org.glite.jp.client/src/tags_plugin.c deleted file mode 100644 index 95dabd8..0000000 --- a/org.glite.jp.client/src/tags_plugin.c +++ /dev/null @@ -1,148 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "file_plugin.h" -#include "builtin_plugins.h" - -static int tagappend(void *,void *,int,...); -static int tagopen(void *,void *,const char *uri,void **); -static int tagclose(void *,void *); - -#define TAGS_MAGIC 0x74c016f2 /* two middle digits encode version, i.e. 01 */ - -static int tagdummy() -{ - puts("tagdummy()"); - return -1; -} - -struct tags_handle { - void *bhandle; - int n; - glite_jp_tagval_t *tags; -}; - -int init(glite_jp_context_t ctx, glite_jpps_fplug_data_t *data) -{ - data->fpctx = ctx; - - data->uris = calloc(2,sizeof *data->uris); - data->uris[0] = strdup(GLITE_JP_FILETYPE_TAGS); - - data->classes = calloc(2,sizeof *data->classes); - data->classes[0] = strdup("tags"); - - data->ops.open = tagopen; - data->ops.close = tagclose; - data->ops.attr = tagdummy; - data->ops.generic = tagappend; - - printf("tags_plugin: URI: \"%s\"; magic number: 0x%08lx\n",GLITE_JP_FILETYPE_TAGS,TAGS_MAGIC); - return 0; -} - -static int tagopen(void *fpctx,void *bhandle,const char *uri,void **handle) -{ - struct tags_handle *h = calloc(1,sizeof *h); - h->n = -1; - h->bhandle = bhandle; - - *handle = h; - - return 0; -} - -static int tagclose(void *fpctx,void *handle) -{ - int i; - struct tags_handle *h = handle; - - for (i=0; in; i++) { - free(h->tags[i].name); - free(h->tags[i].value); - } - free(h->tags); - free(h); - - return 0; -} - -static int tagappend(void *fpctx,void *handle,int oper,...) -{ - glite_jp_tagval_t *tag; - va_list ap; - char *hdr,*rec; - glite_jp_context_t ctx = fpctx; - struct tags_handle *h = handle; - uint32_t magic,hlen,rlen,rlen_n; - size_t r; - glite_jp_error_t err; - - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - glite_jp_clear_error(ctx); - - va_start(ap,oper); - tag = va_arg(ap,glite_jp_tagval_t *); - va_end(ap); - - printf("tagappend: %s,%d,%s\n",tag->name,tag->sequence,tag->value); - - assert(oper == GLITE_JP_FPLUG_TAGS_APPEND); - - if (glite_jppsbe_pread(ctx,h->bhandle,&magic,sizeof magic,0,&r)) { - err.code = EIO; - err.desc = "reading magic number"; - return glite_jp_stack_error(ctx,&err); - } - - if (r == 0) { - magic = htonl(TAGS_MAGIC); - if (glite_jppsbe_pwrite(ctx,h->bhandle,&magic,sizeof magic,0)) { - err.code = EIO; - err.desc = "writing magic number"; - return glite_jp_stack_error(ctx,&err); - } - } - else if (r != sizeof magic) { - err.code = EIO; - err.desc = "can't read magic number"; - return glite_jp_stack_error(ctx,&err); - } - else if (magic != htonl(TAGS_MAGIC)) { - err.code = EINVAL; - err.desc = "invalid magic number"; - return glite_jp_stack_error(ctx,&err); - } - - trio_asprintf(&hdr,"%d %ld %c",tag->sequence, - tag->timestamp,tag->binary ? 'B' : 'S'); - - rlen = strlen(tag->name) + strlen(hdr) + 2 /* \0 after name and after hdr */ + - (r = tag->binary ? tag->size : (tag->value ? strlen(tag->value) : 0)); - - rlen_n = htonl(rlen); - - rec = malloc(rlen + sizeof rlen_n); - *((uint32_t *) rec) = rlen_n; - strcpy(rec + sizeof rlen_n,tag->name); - strcpy(rec + (hlen = sizeof rlen_n + strlen(tag->name) + 1),hdr); - - if (r) memcpy(rec + hlen + strlen(hdr) + 1,tag->value,r); - free(hdr); - - if (glite_jppsbe_append(ctx,h->bhandle,rec,rlen + sizeof rlen_n)) { - err.code = EIO; - err.desc = "writing tag record"; - free(rec); - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} diff --git a/org.glite.jp.client/src/typemap.dat b/org.glite.jp.client/src/typemap.dat deleted file mode 100644 index 72f515f..0000000 --- a/org.glite.jp.client/src/typemap.dat +++ /dev/null @@ -1,3 +0,0 @@ -jpsrv = http://glite.org/wsdl/services/jp -jptype = http://glite.org/wsdl/types/jp -jpelem = http://glite.org/wsdl/elements/jp diff --git a/org.glite.jp.common/.cvsignore b/org.glite.jp.common/.cvsignore deleted file mode 100644 index 3a4edf6..0000000 --- a/org.glite.jp.common/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.project diff --git a/org.glite.jp.common/Makefile b/org.glite.jp.common/Makefile deleted file mode 100644 index 3f84bcd..0000000 --- a/org.glite.jp.common/Makefile +++ /dev/null @@ -1,85 +0,0 @@ -# defaults -top_srcdir=. -builddir=build -top_builddir=${top_srcdir}/${builddir} -stagedir=. -distdir=. -globalprefix=glite -lbprefix=lb -package=glite-jp-common -version=0.0.0 -PREFIX=/opt/glite - -glite_location=/opt/glite -globus_prefix=/opt/globus -nothrflavour=gcc32 -thrflavour=gcc32pthr -expat_prefix=/opt/expat -ares_prefix=/opt/ares -gsoap_prefix=/software/gsoap-2.6 - -CC=gcc - --include Makefile.inc - - -VPATH=${top_srcdir}/src:${top_srcdir}/test:${top_srcdir}/project:${jpproject} - -GLOBUSINC:= -I${globus_prefix}/include/${nothrflavour} - - -DEBUG:=-g -O0 -CFLAGS:=${DEBUG} -I. -I${top_srcdir}/interface -I${stagedir}/include \ - ${GLOBUSINC} - -LINK:=libtool --mode=link ${CC} ${LDFLAGS} -rpath ${stagedir}/lib -LINKXX:=libtool --mode=link ${CXX} ${LDFLAGS} -INSTALL:=libtool --mode=install install -COMPILE:=libtool --mode=compile ${CC} ${CFLAGS} - -HDRS:=types.h context.h strmd5.h - -SRCS:=context.c strmd5.c attr.c -OBJS:=${SRCS:.c=.lo} - -commonlib:= libglite_jp_common.la - -default all: compile - -compile: ${commonlib} - -${commonlib}: ${OBJS} - ${LINK} -o $@ ${OBJS} - -check: - -echo nothing yet - -doc: - -stage: compile - $(MAKE) install PREFIX=${stagedir} - -install: - -mkdir -p ${PREFIX}/include/${globalprefix}/${jpprefix} - cd ${top_srcdir}/interface && install -m 644 ${HDRS} ${PREFIX}/include/${globalprefix}/${jpprefix} - -mkdir -p ${PREFIX}/lib - ${INSTALL} -m 755 ${commonlib} ${PREFIX}/lib - -dist: distsrc distbin - -# FIXME: just copied from LB -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 - -clean: - -%.lo: %.c - ${COMPILE} -o $@ -c $< diff --git a/org.glite.jp.common/build.xml b/org.glite.jp.common/build.xml deleted file mode 100755 index 6e50854..0000000 --- a/org.glite.jp.common/build.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.common/interface/context.h b/org.glite.jp.common/interface/context.h deleted file mode 100644 index 20effb2..0000000 --- a/org.glite.jp.common/interface/context.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef __GLITE_JP_CONTEXT -#define __GLITE_JP_CONTEXT - -int glite_jp_init_context(glite_jp_context_t *); -void glite_jp_free_query_rec(glite_jp_query_rec_t *); - -char *glite_jp_peer_name(glite_jp_context_t); -char *glite_jp_error_chain(glite_jp_context_t); - -int glite_jp_stack_error(glite_jp_context_t, const glite_jp_error_t *); -int glite_jp_clear_error(glite_jp_context_t); - -int glite_jp_add_deferred(glite_jp_context_t,int (*)(glite_jp_context_t,void *),void *); -int glite_jp_run_deferred(glite_jp_context_t); - - -#endif diff --git a/org.glite.jp.common/interface/strmd5.h b/org.glite.jp.common/interface/strmd5.h deleted file mode 100755 index c5d76b6..0000000 --- a/org.glite.jp.common/interface/strmd5.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef _GLITE_STRMD5_H -#define _GLITE_STRMD5_H - -#ident "$Header$" - -/* Compute MD5 sum of the first argument. - * The sum is returned in the 16-byte array pointed to by 2nd argument - * (if not NULL) - * - * Return value: ASCII string of the sum, i.e. 32 characters [0-9a-f] - * (pointer to static area, changed by subsequent calls) - */ - -char *strmd5(const char *src, unsigned char *dst); - -/** - * Returns: allocated 32bytes long ASCII string with md5 sum - * of the first argument - */ -char *str2md5(const char *src); - -/** - * Returns: allocated 22bytes long ASCII string with md5 sum in base64 - * format of the source argument - */ -char *str2md5base64(const char *src); - -#endif /* _GLITE_STRMD5_H */ diff --git a/org.glite.jp.common/interface/type_plugin.h b/org.glite.jp.common/interface/type_plugin.h deleted file mode 100644 index bb34744..0000000 --- a/org.glite.jp.common/interface/type_plugin.h +++ /dev/null @@ -1,49 +0,0 @@ -#ifndef __GLITE_JP_TYPEPLUGIN -#define __GLITE_JP_TYPEPLUGIN - -typedef struct _glite_jp_tplug_data_t { - - char *namespace; - void *pctx; - -/** Compare attribute values. - * \param[in] a value to compare - * \param[in] b value to compare - * \param[out] result like strcmp() - * \param[out] err set if the values cannot be compared - * \retval 0 OK - * \retval other error - */ - int (*cmp)( - void *ctx, - const glite_jp_attrval_t *a, - const glite_jp_attrval_t *b, - int *result); - -/** Convert to and from XML representation */ - char (*to_xml)(void *,const glite_jp_attrval_t *a); - glite_jp_attrval_t (*from_xml)(void *,const char *,const char *); - -/** Convert to and from database string representation */ - char (*to_db)(void *,const glite_jp_attrval_t *a); - glite_jp_attrval_t (*from_db)(void *,const char *); - -/** Query for database type. - * Useful for db column dynamic creation etc. - */ - const char (*db_type)(void *,const glite_jp_attr_t *); - -} glite_jp_tplug_data_t; - -/** Plugin init function. - Must be called init, supposed to be called as many times as required - for different param's (e.g. xsd files). - */ - -typedef int (*glite_jp_tplug_init_t)( - glite_jp_context_t ctx, - const char *param, - glite_jp_tplug_data *plugin_data -); - -#endif diff --git a/org.glite.jp.common/interface/types.h b/org.glite.jp.common/interface/types.h deleted file mode 100644 index 06ba2b3..0000000 --- a/org.glite.jp.common/interface/types.h +++ /dev/null @@ -1,67 +0,0 @@ -#ifndef __GLITE_JP_TYPES -#define __GLITE_JP_TYPES - -#include - -#define GLITE_JP_SYSTEM_NS "http://egee.cesnet.cz/en/WSDL/jp-system" -#define GLITE_JP_ATTR_OWNER GLITE_JP_SYSTEM_NS ":owner" - -typedef struct _glite_jp_error_t { - int code; - const char *desc; - const char *source; - struct _glite_jp_error_t *reason; -} glite_jp_error_t; - -typedef struct _glite_jp_context { - glite_jp_error_t *error; - int (**deferred_func)(struct _glite_jp_context *,void *); - void **deferred_arg; - void *feeds; - struct soap *other_soap; - char *peer; - void **plugins; - void *dbhandle; - char **trusted_peers; -} *glite_jp_context_t; - -typedef enum { - GLITE_JP_ATTR_ORIG_ANY, /**< for queries: don't care about origin */ - GLITE_JP_ATTR_ORIG_SYSTEM, /**< JP internal, e.g. job owner */ - GLITE_JP_ATTR_ORIG_USER, /**< inserted by user explicitely */ - GLITE_JP_ATTR_ORIG_FILE /**< coming from uploaded file */ -} glite_jp_attr_orig_t; - -typedef struct { - char *name; /**< including namespace */ - char *value; - int binary; /**< value is binary */ - size_t size; /**< in case of binary value */ - glite_jp_attr_orig_t origin; - char *origin_detail; /**< where it came from, i.e. file URI:name */ - time_t timestamp; -} glite_jp_attrval_t; - - -typedef enum { - GLITE_JP_QUERYOP_UNDEF, - GLITE_JP_QUERYOP_EQUAL, - GLITE_JP_QUERYOP_UNEQUAL, - GLITE_JP_QUERYOP_LESS, - GLITE_JP_QUERYOP_GREATER, - GLITE_JP_QUERYOP_WITHIN, - GLITE_JP_QUERYOP__LAST, -} glite_jp_queryop_t; - -typedef struct { - char *attr; - glite_jp_queryop_t op; - char *value, *value2; - int binary; - size_t size,size2; - glite_jp_attr_orig_t origin; -} glite_jp_query_rec_t; - -void glite_jp_attrval_free(glite_jp_attrval_t *,int); - -#endif diff --git a/org.glite.jp.common/project/build.number b/org.glite.jp.common/project/build.number deleted file mode 100644 index c680c72..0000000 --- a/org.glite.jp.common/project/build.number +++ /dev/null @@ -1 +0,0 @@ -module.build=36 diff --git a/org.glite.jp.common/project/build.properties b/org.glite.jp.common/project/build.properties deleted file mode 100644 index e69de29..0000000 diff --git a/org.glite.jp.common/project/configure.properties.xml b/org.glite.jp.common/project/configure.properties.xml deleted file mode 100644 index 5f0ea37..0000000 --- a/org.glite.jp.common/project/configure.properties.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - -top_srcdir=.. -builddir=build -stagedir=${stage.abs.dir} -distdir=${dist.dir} -globalprefix=${global.prefix} -jpprefix=${subsystem.prefix} -package=${module.package.name} -PREFIX=${install.dir} -version=${module.version} -glite_location=${with.glite.location} -globus_prefix=${with.globus.prefix} -expat_prefix=${with.expat.prefix} -gsoap_prefix=${with.gsoap.prefix} -ares_prefix=${with.ares.prefix} -thrflavour=${with.globus.thr.flavor} -nothrflavour=${with.globus.nothr.flavor} -cppunit=${with.cppunit.prefix} -jpproject=${subsystem.project.dir} -project=${component.project.dir} - - - diff --git a/org.glite.jp.common/project/properties.xml b/org.glite.jp.common/project/properties.xml deleted file mode 100755 index b9d669d..0000000 --- a/org.glite.jp.common/project/properties.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.common/project/tar_exclude b/org.glite.jp.common/project/tar_exclude deleted file mode 100644 index e1fcd1a..0000000 --- a/org.glite.jp.common/project/tar_exclude +++ /dev/null @@ -1,10 +0,0 @@ -tar_exclude -CVS -build.xml -build -build.properties -properties.xml -configure.properties.xml -.cvsignore -.project -.cdtproject diff --git a/org.glite.jp.common/project/version.properties b/org.glite.jp.common/project/version.properties deleted file mode 100644 index cd1e9e7..0000000 --- a/org.glite.jp.common/project/version.properties +++ /dev/null @@ -1,2 +0,0 @@ -module.version=1.0.0 -module.age=1 diff --git a/org.glite.jp.common/src/attr.c b/org.glite.jp.common/src/attr.c deleted file mode 100644 index 250ef5a..0000000 --- a/org.glite.jp.common/src/attr.c +++ /dev/null @@ -1,13 +0,0 @@ -#include -#include -#include - -#include "types.h" - -void glite_jp_attrval_free(glite_jp_attrval_t *a,int f) -{ - free(a->name); - free(a->value); - free(a->origin_detail); - if (f) free(a); -} diff --git a/org.glite.jp.common/src/context.c b/org.glite.jp.common/src/context.c deleted file mode 100644 index fc7b0bf..0000000 --- a/org.glite.jp.common/src/context.c +++ /dev/null @@ -1,139 +0,0 @@ -#include -#include - -#include "types.h" -#include "context.h" - -int glite_jp_init_context(glite_jp_context_t *ctx) -{ - *ctx = calloc(1,sizeof **ctx); -} - -char *glite_jp_peer_name(glite_jp_context_t ctx) -{ - return strdup(ctx->peer ? ctx->peer : "unknown"); -} - -char *glite_jp_error_chain(glite_jp_context_t ctx) -{ - char *ret = NULL,indent[300] = ""; - int len = 0,add; - char buf[2000]; - - glite_jp_error_t *ep = ctx->error; - - do { - add = snprintf(buf,sizeof buf,"%s%s: %s (%s)\n", - indent, - ep->source, - strerror(ep->code), - ep->desc ? ep->desc : ""); - ret = realloc(ret,len + add + 1); - strncpy(ret + len,buf,add); ret[len += add] = 0; - strcat(indent," "); - } while (ep = ep->reason); - - return ret; -} - -int glite_jp_stack_error(glite_jp_context_t ctx, const glite_jp_error_t *err) -{ - glite_jp_error_t *reason = ctx->error; - - ctx->error = calloc(1,sizeof *ctx->error); - ctx->error->code = err->code; - ctx->error->desc = err->desc ? strdup(err->desc) : NULL; - ctx->error->source = err->source ? strdup(err->source) : NULL; - ctx->error->reason = reason; - - return err->code; -} - -int glite_jp_clear_error(glite_jp_context_t ctx) -{ - glite_jp_error_t *e = ctx->error, *r; - - while (e) { - r = e->reason; - free((char *) e->source); - free((char *) e->desc); - free(e); - e = r; - } - ctx->error = NULL; - return 0; -} - - -void glite_jp_free_query_rec(glite_jp_query_rec_t *q) -{ - free(q->attr); - free(q->value); - free(q->value2); - memset(q,0,sizeof *q); -} - -int glite_jp_queryrec_copy(glite_jp_query_rec_t *dst, const glite_jp_query_rec_t *src) -{ - if (src->attr) dst->attr = strdup(src->attr); - if (src->value) dst->value = strdup(src->value); - if (src->value2) dst->value2 = strdup(src->value2); - dst->op = src->op; - dst->origin = src->origin; - return 0; -} - -int glite_jp_run_deferred(glite_jp_context_t ctx) -{ - int i,cnt,ret; - - if (!ctx->deferred_func) return 0; - - glite_jp_clear_error(ctx); - for (cnt=0;ctx->deferred_func[cnt];cnt++); - for (i=0; ideferred_func)(ctx,*ctx->deferred_arg)) { - glite_jp_error_t err; - char desc[100]; - - sprintf(desc,"calling func #%d, %p",i,*ctx->deferred_func); - err.code = ret; - err.desc = desc; - err.source = "glite_jp_run_deferred()"; - - glite_jp_stack_error(ctx,&err); - return ret; - } - else { - memmove(ctx->deferred_func,ctx->deferred_func+1, - (cnt-i) * sizeof *ctx->deferred_func); - memmove(ctx->deferred_arg,ctx->deferred_arg+1, - (cnt-i) * sizeof *ctx->deferred_arg); - } - } - free(ctx->deferred_func); ctx->deferred_func = NULL; - free(ctx->deferred_arg); ctx->deferred_arg = NULL; - return 0; -} - -int glite_jp_add_deferred( - glite_jp_context_t ctx, - int (*func)(glite_jp_context_t, void *), - void *arg -) -{ - int (**v)(glite_jp_context_t, void *) = ctx->deferred_func; - int i; - - for (i=0; v && *v; i++); - - ctx->deferred_func = realloc(ctx->deferred_func, (i+1) * sizeof *ctx->deferred_func); - ctx->deferred_func[i] = func; - ctx->deferred_func[i+1] = NULL; - - ctx->deferred_arg = realloc(ctx->deferred_arg,(i+1) * sizeof *ctx->deferred_arg); - ctx->deferred_arg[i] = arg; - ctx->deferred_arg[i+1] = NULL; - - return 0; -} diff --git a/org.glite.jp.common/src/stdtypes.c b/org.glite.jp.common/src/stdtypes.c deleted file mode 100644 index 6995e77..0000000 --- a/org.glite.jp.common/src/stdtypes.c +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include - -#include "lb/trio.h" - -#include "types.h" -#include "type_plugin.h" - -static char *namespace = "http://glite.org/wsdl/types/jp_std_attr"; - -static int check_namespace(const glite_jp_attr_t *a) -{ - if (a->namespace && strcmp(a->namespace,namespace)) return -1; - return 0; -} - -static int *cmp( - void *ctx, - const glite_jp_attrval_t *a, - const glite_jp_attrval_t *b, - int *result -{ - struct timeval t; - int r; - - if (check_namespace(&a->attr) || check_namespace(&b->attr)) return -1; - if (glite_jp_attr_cmp(&a->attr,&b->attr)) return -1; - - switch (a->attr.type) { - case GLITE_JP_ATTR_OWNER: - r = strcmp(a->value.s,b->value.s); - break; - case GLITE_JP_ATTR_TIME: - t = a->value.time; - t.tv_sec -= b->value.time.tv_sec; - if ((t.tv_usec -= b->value.time.tv_usec) < 0) { - t.tv_usec += 1000000; - t.tv_sec--; - } - r = t.tv_sec ? t.tv_sec : t.tv_usec; - if (r) r = r > 0 ? 1 : -1; - break; - case GLITE_JP_ATTR_TAG: - if (a->value.tag.binary != b->value.tag.binary) return -1; - if (a->value.tag.binary) { - /* FIXME: I'm lazy. */ - abort(); - } - else r = strcmp(a->value.tag.value,b->value.tag.value); - default: return -1; - } - *result = r; - return 0; -} - -static char *to_xml(void *ctx,const glite_jp_attrval_t *a) -{ - char *out = NULL; - - if (check_namespace(a)) return NULL; - - switch (a->attr.type) { - case GLITE_JP_ATTR_OWNER: - trio_asprintf(&out,"%|Xs",a->value.s); - break; - case GLITE_JP_ATTR_TIME: - /* XXX */ - trio_asprintf(&out,"%ld.06%ld",a->value.time.tv_sec, - a->value.time.tv_usec); - break; - case GLITE_JP_ATTR_TAG: - /* FIXME */ assert(!a->value.tag.binary); - - trio_asprintf(&out,"%d%ld>%|Xs",a->value.tag.sequence,a->value.tag.timestamp,a->value.tag.value); - break; - default: - break; - } - return out; -} - -static glite_jp_attrval_t *from_xml(void *ctx,const char *name,const char *val) -{ - /* FIXME: I'm lazy. */ - abort(); -} - -static char *to_db(void *ctx,const glite_jp_attrval_t *a) -{ - /* FIXME: I'm lazy. */ - abort(); -} - -static glite_jp_attrval_t *from_db(void *ctx,const char *a) -{ - /* FIXME: I'm lazy. */ - abort(); -} - -static const char *db_type(void *ctx,const glite_jp_attr_t *a) -{ - if check_namespace(a) return NULL; - switch (a->type) { - case GLITE_JP_ATTR_OWNER: return "varchar(250) binary"; - case GLITE_JP_ATTR_TIME: return "datetime"; - case GLITE_JP_ATTR_TAG: return "mediumblob"; - default: return NULL; - } -} - -int init( - glite_jp_context_t ctx, - const char *param, - glite_jp_tplug_data *pd -) -{ - pd->namespace = namespace; - pd->cmp = cmp; - pd->to_xml = to_xml; - pd->from_xml = from_xml; - pd->to_db = to_db; - pd->from_db = from_db; - pd->db_type = db_type; - pd->pctx = ctx; -} - diff --git a/org.glite.jp.common/src/strmd5.c b/org.glite.jp.common/src/strmd5.c deleted file mode 100755 index 87fd400..0000000 --- a/org.glite.jp.common/src/strmd5.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include -#include - -#include "strmd5.h" - -static char mbuf[33]; - -static int base64_encode(const void *enc, int enc_size, char *out, int out_max_size) -{ - static const char* b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; - - unsigned char* enc_buf = (unsigned char*)enc; - int out_size = 0; - unsigned int bits = 0; - unsigned int shift = 0; - - while ( out_size < out_max_size ) { - if ( enc_size>0 ) { - // Shift in byte - bits <<= 8; - bits |= *enc_buf; - shift += 8; - // Next byte - enc_buf++; - enc_size--; - } else if ( shift>0 ) { - // Pad last bits to 6 bits - will end next loop - bits <<= 6 - shift; - shift = 6; - } else { - // Terminate with Mime style '=' - *out = '='; - out_size++; - - return out_size; - } - - // Encode 6 bit segments - while ( shift>=6 ) { - shift -= 6; - *out = b64[ (bits >> shift) & 0x3F ]; - out++; - out_size++; - } - } - - // Output overflow - return -1; -} - -char *strmd5(const char *s, unsigned char *digest) -{ - MD5_CTX md5; - unsigned char d[16]; - int i; - - MD5_Init(&md5); - MD5_Update(&md5,s,strlen(s)); - MD5_Final(d,&md5); - - if (digest) memcpy(digest,d,sizeof(d)); - - for (i=0; i<16; i++) { - int dd = d[i] & 0x0f; - mbuf[2*i+1] = dd<10 ? dd+'0' : dd-10+'a'; - dd = d[i] >> 4; - mbuf[2*i] = dd<10 ? dd+'0' : dd-10+'a'; - } - mbuf[32] = 0; - return (char *) mbuf; -} - -char *str2md5(const char *s) -{ - MD5_CTX md5; - unsigned char d[16]; - char* ret = malloc(33); - int i; - - if (!ret) - return NULL; - - MD5_Init(&md5); - MD5_Update(&md5, s, strlen(s)); - MD5_Final(d, &md5); - - for (i=0; i<16; i++) { - int dd = d[i] & 0x0f; - ret[2*i+1] = dd<10 ? dd+'0' : dd-10+'a'; - dd = d[i] >> 4; - ret[2*i] = dd<10 ? dd+'0' : dd-10+'a'; - } - ret[32] = 0; - return ret; -} - -char *str2md5base64(const char *s) -{ - MD5_CTX md5; - unsigned char d[16]; - char buf[50]; - int l; - - MD5_Init(&md5); - MD5_Update(&md5, s, strlen(s)); - MD5_Final(d, &md5); - - l = base64_encode(d, 16, buf, sizeof(buf) - 1); - if (l < 1) - return NULL; - buf[l - 1] = 0; - return strdup(buf); -} diff --git a/org.glite.jp.index/.cvsignore b/org.glite.jp.index/.cvsignore deleted file mode 100644 index 3a4edf6..0000000 --- a/org.glite.jp.index/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.project diff --git a/org.glite.jp.index/Makefile b/org.glite.jp.index/Makefile deleted file mode 100644 index 3b893cd..0000000 --- a/org.glite.jp.index/Makefile +++ /dev/null @@ -1,129 +0,0 @@ -# defaults -top_srcdir=. -builddir=build -top_builddir=${top_srcdir}/${builddir} -stagedir=. -distdir=. -globalprefix=glite -lbprefix=lb -package=glite-lb-server -version=0.0.0 -PREFIX=/opt/glite - -glite_location=/opt/glite -globus_prefix=/opt/globus -nothrflavour=gcc32 -thrflavour=gcc32pthr -expat_prefix=/opt/expat -ares_prefix=/opt/ares -gsoap_prefix=/software/gsoap-2.6 - -CC=gcc - --include Makefile.inc - - -VPATH=${top_srcdir}/src:${top_srcdir}/examples:${top_srcdir}/test:${top_srcdir}/project:${jpproject} - -GLOBUS_LIBS:=-L${globus_prefix}/lib \ - -lglobus_common_${nothrflavour} \ - -lglobus_gssapi_gsi_${nothrflavour} - -DEBUG:=-g -O0 -DDEBUG - -CFLAGS:=${DEBUG} -I. -I${gsoap_prefix}/include -I${stagedir}/include -LDFLAGS:=-L${stagedir}/lib - -LINK:=libtool --mode=link ${CC} ${LDFLAGS} -LINKXX:=libtool --mode=link ${CXX} ${LDFLAGS} -INSTALL:=libtool --mode=install install - - -daemon:=glite-jp-indexd -example:=jpis-test -soap_prefix:=jpis_ - -SRCS:= simple_server.c soap_ops.c \ - ${soap_prefix}C.c \ - ${soap_prefix}Server.c - -EXA_SRCS:=jpis-test.c ${soap_prefix}C.c ${soap_prefix}Client.c - - -OBJS:=${SRCS:.c=.o} stdsoap2.o -EXA_OBJS:=${EXA_SRCS:.c=.o} stdsoap2.o - -COMMONLIB:=-lglite_jp_common - -default all: compile - -compile: ${daemon} ${example} - -${daemon}: ${OBJS} - ${LINK} -o $@ ${OBJS} ${COMMONLIB} ${GLOBUS_LIBS} - -${example}: ${EXA_OBJS} - ${LINK} -o $@ ${EXA_OBJS} - -JobProvenanceIS.xh: JobProvenanceIS.wsdl JobProvenanceTypes.wsdl typemap.dat - cp ${jpproject}/JobProvenanceTypes.wsdl . - ${gsoap_prefix}/bin/wsdl2h -t ${top_srcdir}/src/typemap.dat -c -o $@ $< - rm -f JobProvenanceTypes.wsdl - -${soap_prefix}C.c ${soap_prefix}H.h: JobProvenanceIS.xh - ${gsoap_prefix}/bin/soapcpp2 -w -c -p ${soap_prefix} JobProvenanceIS.xh - -#$(SOAP_PREFIX)H.h $(SOAP_PREFIX)C.c: LB.xh -# $(GSOAP_BIN_PATH)/soapcpp2 -w -c -p $(SOAP_PREFIX) LB.xh -# -#LB.xh: LB.wsdl typemap.dat -# $(GSOAP_BIN_PATH)/wsdl2h -c -o $@ LB.wsdl -# - - -check: - -echo nothing yet - -doc: - -stage: compile - ${INSTALL} -m 755 ${daemon} ${stagedir}/bin - -dist: distsrc distbin - -# FIXME: just copied from LB -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 ${PREFIX}/etc ${PREFIX}/etc/init.d - for p in bkserverd bkindex; do \ - ${INSTALL} -m 755 "glite_lb_$$p" "${PREFIX}/bin/glite-lb-$$p"; \ - done - - for f in dbsetup.sql index.conf.template; do \ - ${INSTALL} -m 644 ${top_srcdir}/config/"glite-lb-$$f" ${PREFIX}/etc; \ - done - - ${INSTALL} -m 755 ${top_srcdir}/config/startup ${PREFIX}/etc/init.d/glite-lb-bkserverd - -clean: - -soap_ops.o jpis-test.o simple_server.o: ${soap_prefix}H.h - -# we have no real config.h but have to force gSoap not to use -# linux ftime with broken (aka obsolete) DST information - -stdsoap2.o: ${gsoap_prefix}/devel/stdsoap2.c - test -f config.h || touch config.h - @echo 'The following warning "time_t (de)serialization is not MT safe on this platform" is harmless' - ${CC} -o $@ -c -DHAVE_CONFIG_H ${CFLAGS} ${gsoap_prefix}/devel/stdsoap2.c - diff --git a/org.glite.jp.index/build.xml b/org.glite.jp.index/build.xml deleted file mode 100755 index 3a97943..0000000 --- a/org.glite.jp.index/build.xml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.index/project/JobProvenanceIS.wsdl b/org.glite.jp.index/project/JobProvenanceIS.wsdl deleted file mode 100644 index 26f97a4..0000000 --- a/org.glite.jp.index/project/JobProvenanceIS.wsdl +++ /dev/null @@ -1,531 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Register job with JP primary storage. -Job registration in LB is propagated to JP immediately so that JP is aware of the job, -despite no furhter information is available in it. - -Input: JobId - -Output: N/A - -Faults: GenericJPFault - - - - - - - Initiate upload of of sandbox/dump of job life log from LB. -WM component responsible for job sandbox management and LB server call JP to declare -intention to upload intput/output sandbox and job life log. - -Input: - -uclass: type of the upload - INPUT_SANDBOX, OUTPUT_SANDBOX, JOB_LOG - -commitTimeout: upper limit on time for which JP waits for committing this upload transaction - -contentType: MIME type of the uploaded file - -Output: - -destination: URL where the client should upload the file - -commitBefore: acutual time when the upload transaction times out - -Faults: GenericJPFault - - -Initiate upload of of sandbox/dump of job life log from LB. - - - - - - Confirm upload. -Should be called after a file upload initiaded with StartUpload is finished. - -Input: - -destination: Upload destination URL (to match with the original request) - -Output: - -Faults: GenericJPFault - - - - - - - Record a value of user tag. -JP tags are either standalone or override values of their LB counterparts. -However, JP tag values are still distinguishable those inherited from LB. -JP tags may be either strings or blobs. - -Input: - -jobid: - -tag: structure containing name, timestamp, optional sequence number to order tag values -without relying on timestamps, and string or blob value. - -Output: N/A - -Faults: GenericJPFault - - - - - - - Start feeding JP index server. -Called by the index server to start batch feed, and optionally also subscribe for incremental feed. - -JP index server subscribes with JP primary storage using a query -containing conditions on primary metadata and a list of queryable attributes -of the index server (i.e. data which should be sent to the index server). - -When a matching job record is created or modified within the primary storage -the job record data are sent to the subscribed index server. - -The subscription is soft-state, it expires after certain time unless refreshed by the client explicitely. - -In the batch mode the query has the same form -with additional flag asking for all matching records (i.e.\ not only -arriving afterwards). - -Input: - -destination: where to send the job record data - -attributes: which job record attributes should be sent to the requesting index server - -conditions: list of query conditions. Each conditions has the form Attribute Operator Value, -where Attribute is any of job record attributes and Operator is one of EQUAL, UNEQUAL, LESS, GREATER, WITHIN. - -continuous: flag determining that the query is incremental (not batch) - -Output: - -feedId: unique Id of the feed request, to be used in refresh, cancelation etc. - -expires: when the feed times out. Must be refreshed before this time. - -Faults: GenericJPFault - - - - - - - Extend batch feed subscription (used by index server) - -Input: feedId returned previously by FeedIndex - -Output: the same as for FeedIndex - -Faults: GenericJPFault - - - - - - - Retrieve job record URL's when jobid is known -Used either to bypass JP index server query for this specific case, or after the index server query to -retrieve actual job record. - -Input: jobid - -Output: - -jobLog, inputSandbox, outputSandbox, tags: URL's to components of the job record. - -Faults: GenericJPFault - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Job Provenance Primary Storage service - - - - - - - - - - - - - - - - - - - - - - Store or update information on jobs within the JP index server. -Called directly by the primary storage, used for both batch and incremental feed. - -Input: - -data: list of job record updates. Each contains jobid, list of JP attribute values and user tag values. - -feedDone: flag indicating end of batch feed. (In order to avoid potential problems with buffer allocation -the huge dataset of batch feed is split into reasonable chunks and delivered with more UpdateJobs calls.) - -Output: N/A - -Faults: GenericJPFault - - - - - - - - - Retrieve pointers to job records of jobs matching a query. -Input: conditions - list of lists of query conditions. - Elements of the inner lists refer to a single job attribute, the conditions are or-ed. - Elements of the outer list may refer to different job attributes, they are and-ed. - -Output: - -jobs: list of JobId, PSContact (URL of the primary storage which manges this job) pairs - -Faults: GenericJPFault - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Job Provenance Index service - - - - - - - diff --git a/org.glite.jp.index/project/build.number b/org.glite.jp.index/project/build.number deleted file mode 100644 index 95388e3..0000000 --- a/org.glite.jp.index/project/build.number +++ /dev/null @@ -1 +0,0 @@ -module.build=9 diff --git a/org.glite.jp.index/project/build.properties b/org.glite.jp.index/project/build.properties deleted file mode 100644 index e69de29..0000000 diff --git a/org.glite.jp.index/project/configure.properties.xml b/org.glite.jp.index/project/configure.properties.xml deleted file mode 100644 index 72af8f4..0000000 --- a/org.glite.jp.index/project/configure.properties.xml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - -top_srcdir=.. -builddir=build -stagedir=${stage.abs.dir} -distdir=${dist.dir} -globalprefix=${global.prefix} -lbprefix=${subsystem.prefix} -package=${module.package.name} -PREFIX=${install.dir} -version=${module.version} -glite_location=${with.glite.location} -globus_prefix=${with.globus.prefix} -expat_prefix=${with.expat.prefix} -gsoap_prefix=${with.gsoap.prefix} -ares_prefix=${with.ares.prefix} -thrflavour=${with.globus.thr.flavor} -nothrflavour=${with.globus.nothr.flavor} -cppunit=${with.cppunit.prefix} -jpproject=${subsystem.project.dir} -project=${component.project.dir} - - - diff --git a/org.glite.jp.index/project/properties.xml b/org.glite.jp.index/project/properties.xml deleted file mode 100755 index 5f56392..0000000 --- a/org.glite.jp.index/project/properties.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.index/project/tar_exclude b/org.glite.jp.index/project/tar_exclude deleted file mode 100644 index e1fcd1a..0000000 --- a/org.glite.jp.index/project/tar_exclude +++ /dev/null @@ -1,10 +0,0 @@ -tar_exclude -CVS -build.xml -build -build.properties -properties.xml -configure.properties.xml -.cvsignore -.project -.cdtproject diff --git a/org.glite.jp.index/project/version.properties b/org.glite.jp.index/project/version.properties deleted file mode 100644 index cd1e9e7..0000000 --- a/org.glite.jp.index/project/version.properties +++ /dev/null @@ -1,2 +0,0 @@ -module.version=1.0.0 -module.age=1 diff --git a/org.glite.jp.index/src/simple_server.c b/org.glite.jp.index/src/simple_server.c deleted file mode 100644 index ccaa5a7..0000000 --- a/org.glite.jp.index/src/simple_server.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "jpis_H.h" - -int main() { - struct soap soap; - int i, m, s; // master and slave sockets - - glite_jp_context_t ctx; - - soap_init(&soap); - glite_jp_init_context(&ctx); - soap.user = (void *) ctx; - - srand48(time(NULL)); /* feed id generation */ - - m = soap_bind(&soap, NULL, 8902, 100); - if (m < 0) - soap_print_fault(&soap, stderr); - else - { - fprintf(stderr, "Socket connection successful: master socket = %d\n", m); - for (i = 1; ; i++) { - s = soap_accept(&soap); - if (s < 0) { - soap_print_fault(&soap, stderr); - break; - } - soap_serve(&soap); // process RPC request - soap_destroy(&soap); // clean up class instances - soap_end(&soap); // clean up everything and close socket - glite_jp_run_deferred(ctx); - } - } - soap_done(&soap); // close master socket - - return 0; -} diff --git a/org.glite.jp.index/src/soap_ops.c b/org.glite.jp.index/src/soap_ops.c deleted file mode 100644 index 5af7352..0000000 --- a/org.glite.jp.index/src/soap_ops.c +++ /dev/null @@ -1,81 +0,0 @@ -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "jpis_H.h" -#include "JobProvenanceIS.nsmap" - -static struct jptype__GenericJPFaultType *jp2s_error(struct soap *soap, - const glite_jp_error_t *err) -{ - struct jptype__GenericJPFaultType *ret = NULL; - if (err) { - ret = soap_malloc(soap,sizeof *ret); - memset(ret,0,sizeof *ret); - ret->code = err->code; - ret->source = soap_strdup(soap,err->source); - ret->text = soap_strdup(soap,strerror(err->code)); - ret->description = soap_strdup(soap,err->desc); - ret->reason = jp2s_error(soap,err->reason); - } - return ret; -} - -static void err2fault(const glite_jp_context_t ctx,struct soap *soap) -{ - char *et; - struct SOAP_ENV__Detail *detail = soap_malloc(soap,sizeof *detail); - struct _GenericJPFault *f = soap_malloc(soap,sizeof *f); - - - f->jptype__GenericJPFault = jp2s_error(soap,ctx->error); - - detail->__type = SOAP_TYPE__GenericJPFault; - detail->value = f; - detail->__any = NULL; - - soap_receiver_fault(soap,"Oh, shit!",NULL); - if (soap->version == 2) soap->fault->SOAP_ENV__Detail = detail; - else soap->fault->detail = detail; -} - -static void s2jp_tag(const struct jptype__TagValue *stag,glite_jp_tagval_t *jptag) -{ - memset(jptag,0,sizeof *jptag); - jptag->name = strdup(stag->name); - jptag->sequence = stag->sequence ? *stag->sequence : 0; - jptag->timestamp = stag->timestamp ? *stag->timestamp : 0; - if (stag->stringValue) jptag->value = strdup(stag->stringValue); - else if (stag->blobValue) { - jptag->binary = 1; - jptag->size = stag->blobValue->__size; - jptag->value = (char *) stag->blobValue->__ptr; - } -} - -#define CONTEXT_FROM_SOAP(soap,ctx) glite_jp_context_t ctx = (glite_jp_context_t) ((soap)->user) - -SOAP_FMAC5 int SOAP_FMAC6 jpsrv__UpdateJobs( - struct soap *soap, - char *feed_id, - struct jptype__UpdateJobsData *jobs, - enum xsd__boolean done -) -{ - printf("%s items %d jobid %s\n",__FUNCTION__,jobs->__sizejob, - jobs->job[0]->jobid); - return SOAP_OK; -} - -SOAP_FMAC5 int SOAP_FMAC6 jpsrv__QueryJobs( - struct soap *soap, - struct jptype__IndexQuery *query, - struct jpsrv__QueryJobsResponse *resp -) -{ - puts(__FUNCTION__); - return SOAP_OK; -} - diff --git a/org.glite.jp.index/src/typemap.dat b/org.glite.jp.index/src/typemap.dat deleted file mode 100644 index 7032cb2..0000000 --- a/org.glite.jp.index/src/typemap.dat +++ /dev/null @@ -1,2 +0,0 @@ -jpsrv = http://glite.org/wsdl/services/jp -jptype = http://glite.org/wsdl/types/jp diff --git a/org.glite.jp.primary/.cvsignore b/org.glite.jp.primary/.cvsignore deleted file mode 100644 index 3a4edf6..0000000 --- a/org.glite.jp.primary/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.project diff --git a/org.glite.jp.primary/Makefile b/org.glite.jp.primary/Makefile deleted file mode 100644 index 73d1c5a..0000000 --- a/org.glite.jp.primary/Makefile +++ /dev/null @@ -1,186 +0,0 @@ -# defaults -top_srcdir=. -builddir=build -top_builddir=${top_srcdir}/${builddir} -stagedir=. -distdir=. -globalprefix=glite -jpprefix=jp -package=glite-jp-primary -version=0.0.0 -PREFIX=/opt/glite - -glite_location=/opt/glite -globus_prefix=/opt/globus -nothrflavour=gcc32 -thrflavour=gcc32pthr -expat_prefix=/opt/expat -ares_prefix=/opt/ares -gsoap_prefix=/software/gsoap-2.6 - -CC=gcc - --include Makefile.inc - - -VPATH=${top_srcdir}/src:${top_srcdir}/examples:${top_srcdir}/test:${top_srcdir}/project:${stagedir}/interface - -GLOBUS_LIBS:=-L${globus_prefix}/lib \ - -lglobus_common_${nothrflavour} \ - -lglobus_gssapi_gsi_${nothrflavour} - -GLOBUS_CFLAGS:=-I${globus_prefix}/include/${nothrflavour} - -DEBUG:=-g -O0 -DDEBUG - -CFLAGS:=${DEBUG} -I. -I${top_srcdir}/interface -I${top_srcdir}/src -I${gsoap_prefix}/include -I${stagedir}/include ${GLOBUS_CFLAGS} -I${mysql_prefix}/include -I${mysql_prefix}/include/mysql -LDFLAGS:=-L${stagedir}/lib - -LINK:=libtool --mode=link ${CC} ${LDFLAGS} -LTCOMPILE:=libtool --mode=compile ${CC} ${CFLAGS} -SOLINK:=libtool --mode=link ${CC} -module ${LDFLAGS} -rpath ${stagedir}/lib -LINKXX:=libtool --mode=link ${CXX} ${LDFLAGS} -INSTALL:=libtool --mode=install install - -daemon:=glite-jp-primarystoraged -example:=jpps-test -ps_prefix:=jpps_ -is_prefix:=jpis_ - -plugins:=glite-jp-tags.la - -HDRS_I=file_plugin.h -HDRS_S=builtin_plugins.h backend.h - -SRCS:= bones_server.c soap_ops.c \ - new_ftp_backend.c mysql.c file_plugin.c \ - feed.c authz.c\ - is_client.c \ - ${ps_prefix}ServerLib.c \ - ${is_prefix}ClientLib.c jpps_C.c -# env_C.c - -EXA_SRCS:=jpps-test.c ${ps_prefix}C.c ${ps_prefix}Client.c - - -OBJS:=${SRCS:.c=.o} -EXA_OBJS:=${EXA_SRCS:.c=.o} - -COMMONLIB:=-lglite_jp_common -BONESLIB:=-lglite_lb_server_bones -GSOAPLIB:=-lglite_security_gsoap_plugin_${nothrflavour} -lglite_security_gss_${nothrflavour} \ - -L${gsoap_prefix}/lib -lgsoap${GSOAP_DEBUG} -L${ares_prefix}/lib -lares -TRIOLIB:=-lglite_lb_trio - -ifneq (${mysql_prefix},/usr) - ifeq ($(shell echo ${mysql_version} | cut -d. -f1,2),4.1) - MYSQLIB := -L${mysql_prefix}/lib/mysql -lmysqlclient - else - MYSQLIB := -L${mysql_prefix}/lib -lmysqlclient - endif -else - MYSQLIB := -lmysqlclient -endif - -default all: compile - -compile: ${daemon} ${example} ${plugins} - -${daemon}: ${OBJS} - ${LINK} -o $@ -export-dynamic ${OBJS} ${BONESLIB} ${TRIOLIB} ${COMMONLIB} ${GSOAPLIB} ${GLOBUS_LIBS} ${MYSQLIB} - -${example}: ${EXA_OBJS} - ${LINK} -o $@ ${EXA_OBJS} ${GSOAPLIB} ${GLOBUS_LIBS} - -# XXX: piss off -JobProvenanceIS.xh: - touch $@ - -JobProvenancePS.xh: %.xh: %.wsdl JobProvenanceTypes.wsdl typemap.dat - cp ${stagedir}/interface/JobProvenanceTypes.wsdl . - ${gsoap_prefix}/bin/wsdl2h -t ${top_srcdir}/src/typemap.dat -c -o $@ $< - rm -f JobProvenanceTypes.wsdl - -${ps_prefix}Client.c ${ps_prefix}ClientLib.c \ -${ps_prefix}Server.c ${ps_prefix}ServerLib.c \ -${ps_prefix}C.c ${ps_prefix}H.h: JobProvenancePS.xh - ${gsoap_prefix}/bin/soapcpp2 -n -w -c -p ${ps_prefix} JobProvenancePS.xh - -${is_prefix}ClientLib.c ${is_prefix}Client.c \ -${is_prefix}C.c ${is_prefix}H.h: JobProvenanceIS.xh - ${gsoap_prefix}/bin/soapcpp2 -n -w -c -p ${is_prefix} JobProvenanceIS.xh - -env_C.c env_Server.c: - touch env.xh - cp ${jpproject}/JobProvenanceTypes.wsdl . - ${gsoap_prefix}/bin/wsdl2h -t ${top_srcdir}/src/typemap.dat -c -o env.xh JobProvenanceTypes.wsdl - rm -f JobProvenanceTypes.wsdl - ${gsoap_prefix}/bin/soapcpp2 -w -c -p env_ env.xh - -#$(SOAP_PREFIX)H.h $(SOAP_PREFIX)C.c: LB.xh -# $(GSOAP_BIN_PATH)/soapcpp2 -w -c -p $(SOAP_PREFIX) LB.xh -# -#LB.xh: LB.wsdl typemap.dat -# $(GSOAP_BIN_PATH)/wsdl2h -c -o $@ LB.wsdl -# - - -bones_server.o simple_server.o: ${is_prefix}H.h ${ps_prefix}H.h - -check: - -echo nothing yet - -doc: - -stage: compile - ${MAKE} PREFIX=${stagedir} DOSTAGE=yes install - -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 ${PREFIX}/etc ${PREFIX}/examples ${PREFIX}/etc/init.d - ${INSTALL} -m 755 ${daemon} ${PREFIX}/bin - ${INSTALL} -m 755 jpps-test ${PREFIX}/examples/glite-jp-primary-test - if [ x${DOSTAGE} = xyes ]; then \ - mkdir -p ${PREFIX}/include/${globalprefix}/${jpprefix} ; \ - (cd ${top_srcdir}/interface && install -m 644 ${HDRS_I} ${PREFIX}/include/${globalprefix}/${jpprefix}) ; \ - (cd ${top_srcdir}/src && install -m 644 ${HDRS_S} ${PREFIX}/include/${globalprefix}/${jpprefix}) ; \ - fi - - -clean: - -simple_server.o soap_ops.o jpps-test.o: ${ps_prefix}H.h - -# we have no real config.h but have to force gSoap not to use -# linux ftime with broken (aka obsolete) DST information - -stdsoap2.o: ${gsoap_prefix}/devel/stdsoap2.c - test -f config.h || touch config.h - @echo 'The following warning "time_t (de)serialization is not MT safe on this platform" is harmless' - ${CC} -o $@ -c -DWITH_NONAMESPACES -DHAVE_CONFIG_H ${CFLAGS} ${gsoap_prefix}/devel/stdsoap2.c - - -glite-jp-tags.la: tags_plugin.lo - ${SOLINK} -o $@ tags_plugin.lo - -%.lo: %.c - ${LTCOMPILE} -o $@ -c $< - -soap_ops.o bones_server.o: soap_version.h - -soap_version.h: - ${gsoap_prefix}/bin/soapcpp2 /dev/null - perl -ne '$$. == 2 && /.*([0-9])\.([0-9])\.([0-9]).*/ && printf "#define GSOAP_VERSION %d%02d%02d\n",$$1,$$2,$$3' soapH.h >$@ - -rm soapC.cpp soapH.h soapStub.h soapClient.cpp soapServer.cpp soapClientLib.cpp soapServerLib.cpp diff --git a/org.glite.jp.primary/build.xml b/org.glite.jp.primary/build.xml deleted file mode 100755 index 439631b..0000000 --- a/org.glite.jp.primary/build.xml +++ /dev/null @@ -1,102 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.primary/config/glite-jp-primary-dbsetup.sql b/org.glite.jp.primary/config/glite-jp-primary-dbsetup.sql deleted file mode 100644 index f253f1d..0000000 --- a/org.glite.jp.primary/config/glite-jp-primary-dbsetup.sql +++ /dev/null @@ -1,46 +0,0 @@ -create table jobs ( - jobid char(32) binary not null, - dg_jobid varchar(255) binary not null, - owner char(32) binary not null, - - reg_time datetime not null, - - primary key (jobid), - unique (dg_jobid), - index (owner), - index (owner,reg_time) -); - -create table files ( - jobid char(32) binary not null, - filename varchar(255) binary not null, - int_path mediumblob null, - ext_url mediumblob null, - - state char(32) binary not null, - deadline datetime null, - ul_userid char(32) binary not null, - - primary key (jobid,filename), - index (ext_url(255)) -); - -create table attrs ( - jobid char(32) binary not null, - name varchar(255) binary not null, - value mediumblob null, - - primary key (jobid,name) -); - -create table users ( - userid char(32) binary not null, - cert_subj varchar(255) binary not null, - - primary key (userid), - unique (cert_subj) -); - -create table backend_info ( - version char(32) binary not null -); diff --git a/org.glite.jp.primary/examples/README.test b/org.glite.jp.primary/examples/README.test deleted file mode 100644 index 6bfe1d8..0000000 --- a/org.glite.jp.primary/examples/README.test +++ /dev/null @@ -1,40 +0,0 @@ -Create database: ----------------- - -$ mysqladmin -u root -p create jpps -$ mysql -u root -p -mysql> grant all privileges on jpps.* to jpps@localhost identified by ''; - -$ mysql -p -u jpps jpps -#include -#include -#include - -#include "glite/security/glite_gsplugin.h" - -#include "jpps_H.h" -#include "jpps_.nsmap" - -#include "jptype_map.h" - -#include "soap_version.h" -#if GSOAP_VERSION <= 20602 -#define soap_call___jpsrv__RegisterJob soap_call___ns1__RegisterJob -#define soap_call___jpsrv__StartUpload soap_call___ns1__StartUpload -#define soap_call___jpsrv__CommitUpload soap_call___ns1__CommitUpload -#define soap_call___jpsrv__RecordTag soap_call___ns1__RecordTag -#define soap_call___jpsrv__FeedIndex soap_call___ns1__FeedIndex -#define soap_call___jpsrv__FeedIndexRefresh soap_call___ns1__FeedIndexRefresh -#define soap_call___jpsrv__GetJob soap_call___ns1__GetJob -#endif - - -static void usage(const char *me) -{ - fprintf(stderr,"%s: [-s server-url] operation args \n\n" - " operations are:\n" - " RegisterJob jobid owner\n" - " StartUpload jobid class commit_before mimetype\n" - " CommitUpload destination\n" - " RecordTag jobid tagname sequence stringvalue\n" - " GetJob jobid\n" - " FeedIndex destination query_number history continuous\n" - " FeedIndexRefresh feedid\n" - ,me); - - exit (EX_USAGE); -} - -static int check_fault(struct soap *soap,int err) { - struct SOAP_ENV__Detail *detail; - struct jptype__genericFault *f; - char *reason,indent[200] = " "; - - switch(err) { - case SOAP_OK: puts("OK"); - break; - case SOAP_FAULT: - case SOAP_SVR_FAULT: - if (soap->version == 2) { - detail = soap->fault->SOAP_ENV__Detail; - reason = soap->fault->SOAP_ENV__Reason; - } - else { - detail = soap->fault->detail; - reason = soap->fault->faultstring; - } - fputs(reason,stderr); - putc('\n',stderr); - assert(detail->__type == SOAP_TYPE__genericFault); -#if GSOAP_VERSION >=20700 - f = ((struct _genericFault *) detail->fault) -#else - f = ((struct _genericFault *) detail->value) -#endif - -> jpelem__genericFault; - - while (f) { - fprintf(stderr,"%s%s: %s (%s)\n",indent, - f->source,f->text,f->description); - f = f->reason; - strcat(indent," "); - } - return -1; - - default: soap_print_fault(soap,stderr); - return -1; - } - return 0; -} - -/* FIXME: new wsdl */ -#if 0 -static struct jptype__Attribute sample_attr[] = { - { OWNER, NULL }, - { TIME, "submitted" }, - { TAG, "test" }, -}; - -static struct jptype__PrimaryQueryElement sample_query[][5] = { - { - { sample_attr+OWNER, EQUAL, "unknown", NULL }, - { NULL, 0, NULL, NULL } - }, -}; -#endif - -int main(int argc,char *argv[]) -{ - char *server = "http://localhost:8901"; - int opt; - struct soap *soap = soap_new(); - - if (argc < 2) usage(argv[0]); - - soap_init(soap); - soap_set_namespaces(soap, jpps__namespaces); - - soap_register_plugin(soap,glite_gsplugin); - - while ((opt = getopt(argc,argv,"s:")) >= 0) switch (opt) { - case 's': server = optarg; - argv += 2; - break; - case '?': usage(argv[0]); - } - - if (!strcasecmp(argv[1],"RegisterJob")) { - struct _jpelem__RegisterJob in; - struct _jpelem__RegisterJobResponse empty; - - if (argc != 4) usage(argv[0]); - in.job = argv[2]; - in.owner = argv[3]; - check_fault(soap, - soap_call___jpsrv__RegisterJob(soap,server,"",&in,&empty)); - } else if (!strcasecmp(argv[1], "StartUpload")) { - struct _jpelem__StartUpload in; - struct _jpelem__StartUploadResponse out; - - in.job = argv[2]; - in.class_ = argv[3]; - in.name = NULL; - in.commitBefore = atoi(argv[4]) + time(NULL); - in.contentType = argv[5]; - - if (argc != 6) usage(argv[0]); - if (!check_fault(soap, - soap_call___jpsrv__StartUpload(soap, server, "",&in,&out))) - { - printf("Destination: %s\nCommit before: %s\n", out.destination, ctime(&out.commitBefore)); - } - } else if (!strcasecmp(argv[1], "CommitUpload")) { - struct _jpelem__CommitUpload in; - struct _jpelem__CommitUploadResponse empty; - - in.destination = argv[2]; - - if (argc != 3) usage(argv[0]); - if (!check_fault(soap, - soap_call___jpsrv__CommitUpload(soap, server, "",&in,&empty))) { - /* OK */ - } - } else if (!strcasecmp(argv[1], "RecordTag")) { - struct _jpelem__RecordTag in; - struct _jpelem__RecordTagResponse empty; - struct jptype__tagValue tagval; - - int seq = 0; - - if (argc != 6) usage(argv[0]); - - in.jobid = argv[2]; - in.tag = &tagval; - tagval.name = argv[3]; - seq = atoi(argv[4]); - tagval.sequence = &seq; - tagval.timestamp = NULL; - tagval.stringValue = argv[5]; - tagval.blobValue = NULL; - - if (!check_fault(soap, - soap_call___jpsrv__RecordTag(soap, server, "",&in, &empty))) { - /* OK */ - } - } -/* FIXME: new wsdl */ -#if 0 - else if (!strcasecmp(argv[1],"FeedIndex")) { - struct jpsrv__FeedIndexResponse r; - struct jptype__Attribute *ap[2]; - struct jptype__Attributes attr = { 2, ap }; - struct jptype__PrimaryQueryElement *qp[100]; - struct jptype__PrimaryQuery qry = { 0, qp }; - - int i,j,qi = atoi(argv[3])-1; - - if (argc != 6) usage(argv[0]); - - for (i=0; iclass_, - out.files[i]->name, - out.files[i]->url); - } - } - - } - else usage(argv[0]); - - return 0; -} - - -/* XXX: we don't use it */ -SOAP_NMAC struct Namespace namespaces[] = { {NULL,NULL} }; diff --git a/org.glite.jp.primary/interface/file_plugin.h b/org.glite.jp.primary/interface/file_plugin.h deleted file mode 100644 index eae219d..0000000 --- a/org.glite.jp.primary/interface/file_plugin.h +++ /dev/null @@ -1,73 +0,0 @@ -#ifndef __GLITE_JP_FILEPLUGIN -#define __GLITE_JP_FILEPLUGIN - -/** Methods of the file plugin. */ - -typedef struct _glite_jpps_fplug_op_t { - -/** Open a file. -\param[in] fpctx Context of the plugin, returned by its init. -\param[in] bhandle Handle of the file via JPPS backend. -\param[in] uri URI (type) of the opened file. -\param[out] handle Handle to the opened file structure, to be passed to other plugin functions. -*/ - int (*open)(void *fpctx,void *bhandle,const char *uri,void **handle); - -/** Close the file. Free data associated to a handle */ - int (*close)(void *fpctx,void *handle); - -/** Retrieve value(s) of an attribute. -\param[in] fpctx Plugin context. -\param[in] handle Handle of the opened file. -\param[in] attr Queried attribute. -\param[out] attrval GLITE_JP_ATTR_UNDEF-terminated list of value(s) of the attribute. - If there are more and there is an interpretation of their order - they must be sorted, eg. current value of tag is the last one. -\retval 0 success -\retval ENOSYS this attribute is not defined by this type of file -\retval ENOENT no value is present -*/ - int (*attr)(void *fpctx,void *handle,const char *attr,glite_jp_attrval_t **attrval); - -/** File type specific operation. -\param[in] fpctx Plugin context. -\param[in] handle Handle of the opened file. -\param[in] oper Code of the operation, specific for a concrete plugin. -*/ - int (*generic)(void *fpctx,void *handle,int oper,...); - -} glite_jpps_fplug_op_t; - -/** Data describing a plugin. */ -typedef struct _glite_jpps_fplug_data_t { - void *fpctx; /**< Context passed to plugin operations. */ - char **uris; /**< NULL-terminated list of file types (URIs) - handled by the plugin. */ - char **classes; /**< The same as uris but filesystem-friendly - (can be used to construct file names).*/ - char **namespaces; /**< Which attribute namespaces this plugin handles. */ - - glite_jpps_fplug_op_t ops; /**< Plugin operations. */ -} glite_jpps_fplug_data_t; - -/** Initialisation function of the plugin. - Called after dlopen(), must be named "init". -\param[in] ctx JPPS context -\param[out] data filled-in plugin data -*/ - -typedef int (*glite_jpps_fplug_init_t)( - glite_jp_context_t ctx, - glite_jpps_fplug_data_t *plugin_data -); - - - - -/* XXX: not really public interface follows */ - -int glite_jpps_fplug_load(glite_jp_context_t ctx,int argc,char **argv); -int glite_jpps_fplug_lookup(glite_jp_context_t ctx,const char *uri, glite_jpps_fplug_data_t ***plugin_data); -int glite_jpps_fplug_lookup_byclass(glite_jp_context_t, const char *class,glite_jpps_fplug_data_t ***plugin_data,char **uri); - -#endif diff --git a/org.glite.jp.primary/project/build.number b/org.glite.jp.primary/project/build.number deleted file mode 100644 index c680c72..0000000 --- a/org.glite.jp.primary/project/build.number +++ /dev/null @@ -1 +0,0 @@ -module.build=36 diff --git a/org.glite.jp.primary/project/build.properties b/org.glite.jp.primary/project/build.properties deleted file mode 100644 index e69de29..0000000 diff --git a/org.glite.jp.primary/project/configure.properties.xml b/org.glite.jp.primary/project/configure.properties.xml deleted file mode 100644 index 5e1ce29..0000000 --- a/org.glite.jp.primary/project/configure.properties.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - -top_srcdir=.. -builddir=build -stagedir=${stage.abs.dir} -distdir=${dist.dir} -globalprefix=${global.prefix} -lbprefix=${subsystem.prefix} -package=${module.package.name} -PREFIX=${install.dir} -version=${module.version} -glite_location=${with.glite.location} -globus_prefix=${with.globus.prefix} -expat_prefix=${with.expat.prefix} -ares_prefix=${with.ares.prefix} -gsoap_prefix=${with.gsoap.prefix} -mysql_prefix=${with.mysql.prefix} -mysql_version=${ext.mysql.version} -thrflavour=${with.globus.thr.flavor} -nothrflavour=${with.globus.nothr.flavor} -cppunit=${with.cppunit.prefix} -jpproject=${subsystem.project.dir} -project=${component.project.dir} - - - diff --git a/org.glite.jp.primary/project/properties.xml b/org.glite.jp.primary/project/properties.xml deleted file mode 100755 index 2149dd4..0000000 --- a/org.glite.jp.primary/project/properties.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.primary/project/tar_exclude b/org.glite.jp.primary/project/tar_exclude deleted file mode 100644 index e1fcd1a..0000000 --- a/org.glite.jp.primary/project/tar_exclude +++ /dev/null @@ -1,10 +0,0 @@ -tar_exclude -CVS -build.xml -build -build.properties -properties.xml -configure.properties.xml -.cvsignore -.project -.cdtproject diff --git a/org.glite.jp.primary/project/version.properties b/org.glite.jp.primary/project/version.properties deleted file mode 100644 index cd1e9e7..0000000 --- a/org.glite.jp.primary/project/version.properties +++ /dev/null @@ -1,2 +0,0 @@ -module.version=1.0.0 -module.age=1 diff --git a/org.glite.jp.primary/src/authz.c b/org.glite.jp.primary/src/authz.c deleted file mode 100644 index 3ceba02..0000000 --- a/org.glite.jp.primary/src/authz.c +++ /dev/null @@ -1,78 +0,0 @@ -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "jpps_H.h" -#include "jptype_map.h" - -int glite_jpps_authz(glite_jp_context_t ctx,int op,const char *job,const char *owner) -{ - glite_jp_error_t err; - char buf[200]; - int i; - - memset(&err,0,sizeof err); - glite_jp_clear_error(ctx); - err.source = __FUNCTION__; - err.code = EPERM; - - switch (op) { - case SOAP_TYPE___jpsrv__RegisterJob: - case SOAP_TYPE___jpsrv__StartUpload: - case SOAP_TYPE___jpsrv__CommitUpload: - for (i=0; ctx->trusted_peers && ctx->trusted_peers[i]; i++) - if (!strcmp(ctx->trusted_peers[i],ctx->peer)) return 0; - err.desc = "you are not a trusted peer"; - return glite_jp_stack_error(ctx,&err); - - case SOAP_TYPE___jpsrv__GetJobFiles: - case SOAP_TYPE___jpsrv__GetJobAttributes: - assert(owner); - return strcmp(owner,ctx->peer) ? glite_jp_stack_error(ctx,&err) : 0; - break; - - default: - snprintf(buf,sizeof buf,"%d: unknown operation",op); - err.desc = buf; - err.code = EINVAL; - return glite_jp_stack_error(ctx,&err); - } -} - -int glite_jpps_readauth(glite_jp_context_t ctx,const char *file) -{ - FILE *f = fopen(file,"r"); - glite_jp_error_t err; - int cnt = 0; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - if (!f) { - err.code = errno; - err.desc = file; - return glite_jp_stack_error(ctx,&err); - } - - ctx->trusted_peers = NULL; - while (!feof(f)) { - char buf[BUFSIZ]; - - if (fscanf(f,"%[^\n]\n",buf) != 1) { - err.code = EINVAL; - err.desc = file; - fclose(f); - return glite_jp_stack_error(ctx,&err); - } - - ctx->trusted_peers = realloc(ctx->trusted_peers, (cnt+2) * sizeof *ctx->trusted_peers); - ctx->trusted_peers[cnt++] = strdup(buf); - ctx->trusted_peers[cnt] = NULL; - } - fclose(f); - return 0; -} diff --git a/org.glite.jp.primary/src/authz.h b/org.glite.jp.primary/src/authz.h deleted file mode 100644 index 9451aef..0000000 --- a/org.glite.jp.primary/src/authz.h +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Check authorisation of JPPS operation on job. - * - * \param[in] ctx JP context including peer name & other credentials (VOMS etc.) - * \param[in] op operation, one of SOAP_TYPE___jpsrv__* - * \param[in] job jobid of the job to decide upon - * \param[in] owner current known owner of the job (may be NULL), shortcut to avoid - * unnecessary database query. - * - * \retval 0 OK, operation permitted - * \retval EPERM denied - * \retval other error - */ - -int glite_jpps_authz(glite_jp_context_t ctx,int op,const char *job,const char *owner); - -int glite_jpps_readauth(glite_jp_context_t ctx,const char *file); - diff --git a/org.glite.jp.primary/src/backend.h b/org.glite.jp.primary/src/backend.h deleted file mode 100644 index cf901fb..0000000 --- a/org.glite.jp.primary/src/backend.h +++ /dev/null @@ -1,116 +0,0 @@ -#ifndef __GLITE_JP_BACKEND -#define __GLITE_JP_BACKEND - -#include -#include - -int glite_jppsbe_init( - glite_jp_context_t ctx, - int argc, - char *argv[] -); - -int glite_jppsbe_init_slave( - glite_jp_context_t ctx -); - -int glite_jppsbe_register_job( - glite_jp_context_t ctx, - const char *job, - const char *owner -); - -int glite_jppsbe_start_upload( - glite_jp_context_t ctx, - const char *job, - const char *class, /* must be filesystem-friendly */ - const char *name, /* optional name within the class */ - const char *content_type, - char **destination_out, - time_t *commit_before_inout -); - -int glite_jppsbe_commit_upload( - glite_jp_context_t ctx, - const char *destination -); - -int glite_jppsbe_get_names( - glite_jp_context_t ctx, - const char *job, - const char *class, - char ***names_out -); - -int glite_jppsbe_destination_info( - glite_jp_context_t ctx, - const char *destination, - char **job_out, - char **class_out, - char **name_out -); - -int glite_jppsbe_get_job_url( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* optional within class */ - char **url_out -); - -int glite_jppsbe_open_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* optional within class */ - int mode, - void **handle_out -); - -int glite_jppsbe_close_file( - glite_jp_context_t ctx, - void *handle -); - -int glite_jppsbe_pread( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset, - ssize_t *nbytes_ret -); - -int glite_jppsbe_pwrite( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset -); - -int glite_jppsbe_append( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes -); - -int glite_jppsbe_get_job_metadata( - glite_jp_context_t ctx, - const char *job, - glite_jp_attrval_t attrs_inout[] -); - -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -); - -#endif diff --git a/org.glite.jp.primary/src/bones_server.c b/org.glite.jp.primary/src/bones_server.c deleted file mode 100644 index dcc31d6..0000000 --- a/org.glite.jp.primary/src/bones_server.c +++ /dev/null @@ -1,333 +0,0 @@ -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "glite/lb/srvbones.h" -#include "glite/security/glite_gss.h" - -#include -#include "glite/security/glite_gsplugin.h" - -#include "backend.h" -#include "file_plugin.h" - -#include "soap_version.h" -#include "jpps_H.h" - -#define CONN_QUEUE 20 - -extern SOAP_NMAC struct Namespace jpis__namespaces[],jpps__namespaces[]; - -static int newconn(int,struct timeval *,void *); -static int request(int,struct timeval *,void *); -static int reject(int); -static int disconn(int,struct timeval *,void *); -static int data_init(void **data); - -static struct glite_srvbones_service stab = { - "JP Primary Storage", -1, newconn, request, reject, disconn -}; - -static time_t cert_mtime; -static char *server_cert, *server_key, *cadir; -static gss_cred_id_t mycred = GSS_C_NO_CREDENTIAL; -static char *mysubj; - -static char *port = "8901"; -static int debug = 1; - -static glite_jp_context_t ctx; - -static int call_opts(glite_jp_context_t,char *,char *,int (*)(glite_jp_context_t,int,char **)); - -char *glite_jp_default_namespace; - -int main(int argc, char *argv[]) -{ - int one = 1,opt,i; - edg_wll_GssStatus gss_code; - struct sockaddr_in a; - char *b_argv[20] = { "backend" },*p_argv[20] = { "plugins" },*com; - int b_argc,p_argc; - - glite_jp_init_context(&ctx); - - b_argc = p_argc = 1; - - while ((opt = getopt(argc,argv,"B:P:a:")) != EOF) switch (opt) { - case 'B': - assert(b_argc < 20); - if (com = strchr(optarg,',')) *com = 0; - - /* XXX: memleak -- who cares for once */ - asprintf(&b_argv[b_argc++],"-%s",optarg); - if (com) b_argv[b_argc++] = com+1; - - break; - case 'P': - assert(p_argc < 20); - p_argv[p_argc++] = optarg; - - break; - case 'a': - if (glite_jpps_readauth(ctx,optarg)) { - fprintf(stderr,"%s: %s\n",argv[0],glite_jp_error_chain(ctx)); - exit (1); - } - break; - case '?': fprintf(stderr,"usage: %s: -Bb,val ... -Pplugin.so ...\n" - "b is backend option\n",argv[0]); - exit (1); - } - - if (b_argc == 1) { - fputs("-B required\n",stderr); - exit (1); - } - - optind = 0; /* XXX: getopt used internally */ - if (glite_jppsbe_init(ctx,b_argc,b_argv)) { - fputs(glite_jp_error_chain(ctx), stderr); - exit(1); - } - - optind = 0; /* XXX: getopt used internally */ - if (b_argc > 1 && glite_jpps_fplug_load(ctx,p_argc,p_argv)) { - fputs(glite_jp_error_chain(ctx), stderr); - exit(1); - } - - srand48(time(NULL)); /* feed id generation */ - -#if GSOAP_VERSION <= 20602 - for (i=0; jpps__namespaces[i].id && strcmp(jpps__namespaces[i].id,"ns1"); i++); -#else - for (i=0; jpps__namespaces[i].id && strcmp(jpps__namespaces[i].id,"jpsrv"); i++); -#endif - assert(jpps__namespaces[i].id); - glite_jp_default_namespace = jpps__namespaces[i].ns; - - stab.conn = socket(PF_INET, SOCK_STREAM, 0); - if (stab.conn < 0) { - perror("socket"); - return 1; - } - - setsockopt(stab.conn,SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - - a.sin_family = AF_INET; - a.sin_addr.s_addr = INADDR_ANY; - a.sin_port = htons(atoi(port)); - if (bind(stab.conn,(struct sockaddr *) &a, sizeof(a)) ) { - char buf[200]; - - snprintf(buf,sizeof(buf),"bind(%d)",atoi(port)); - perror(buf); - return 1; - } - - if (listen(stab.conn,CONN_QUEUE)) { - perror("listen()"); - return 1; - } - - if (!server_cert || !server_key) - fprintf(stderr, "%s: WARNING: key or certificate file not specified, " - "can't watch them for changes\n", - argv[0]); - - if ( cadir ) setenv("X509_CERT_DIR", cadir, 1); - edg_wll_gss_watch_creds(server_cert, &cert_mtime); - - if ( !edg_wll_gss_acquire_cred_gsi(server_cert, server_key, &mycred, &mysubj, &gss_code)) - fprintf(stderr,"Server idenity: %s\n",mysubj); - else fputs("WARNING: Running unauthenticated\n",stderr); - - /* XXX: daemonise */ - - glite_srvbones_set_param(GLITE_SBPARAM_SLAVES_COUNT,1); - glite_srvbones_run(data_init,&stab,1 /* XXX: entries in stab */,debug); - - return 0; -} - -static int data_init(void **data) -{ - *data = (void *) soap_new(); - - printf("[%d] slave started\n",getpid()); - glite_jppsbe_init_slave(ctx); /* XXX: global but slave's */ - - return 0; -} - -static int newconn(int conn,struct timeval *to,void *data) -{ - struct soap *soap = (struct soap *) data; - glite_gsplugin_Context plugin_ctx; - - gss_cred_id_t newcred = GSS_C_NO_CREDENTIAL; - edg_wll_GssStatus gss_code; - gss_name_t client_name = GSS_C_NO_NAME; - gss_buffer_desc token = GSS_C_EMPTY_BUFFER; - OM_uint32 maj_stat,min_stat; - - - int ret = 0; - - soap_init2(soap,SOAP_IO_KEEPALIVE,SOAP_IO_KEEPALIVE); - soap_set_namespaces(soap,jpps__namespaces); - soap->user = (void *) ctx; /* XXX: one instance per slave */ - -/* not yet: client to JP index - ctx->other_soap = soap_new(); - soap_init(ctx->other_soap); - soap_set_namespaces(ctx->other_soap,jpis__namespaces); -*/ - - - glite_gsplugin_init_context(&plugin_ctx); - plugin_ctx->connection = calloc(1,sizeof *plugin_ctx->connection); - soap_register_plugin_arg(soap,glite_gsplugin,plugin_ctx); - - switch (edg_wll_gss_watch_creds(server_cert,&cert_mtime)) { - case 0: break; - case 1: if (!edg_wll_gss_acquire_cred_gsi(server_cert,server_key, - &newcred,NULL,&gss_code)) - { - - printf("[%d] reloading credentials\n",getpid()); /* XXX: log */ - gss_release_cred(&min_stat,&mycred); - mycred = newcred; - } - break; - case -1: - printf("[%d] edg_wll_gss_watch_creds failed\n", getpid()); /* XXX: log */ - break; - } - - /* TODO: DNS paranoia etc. */ - - if (edg_wll_gss_accept(mycred,conn,to,plugin_ctx->connection,&gss_code)) { - printf("[%d] GSS connection accept failed, closing.\n", getpid()); - ret = 1; - goto cleanup; - } - - maj_stat = gss_inquire_context(&min_stat,plugin_ctx->connection->context, - &client_name, NULL, NULL, NULL, NULL, NULL, NULL); - - if (!GSS_ERROR(maj_stat)) - maj_stat = gss_display_name(&min_stat,client_name,&token,NULL); - - if (ctx->peer) free(ctx->peer); - if (!GSS_ERROR(maj_stat)) { - printf("[%d] client DN: %s\n",getpid(),(char *) token.value); /* XXX: log */ - - ctx->peer = strdup(token.value); - memset(&token, 0, sizeof(token)); - } - else { - printf("[%d] annonymous client\n",getpid()); - ctx->peer = NULL; - } - - if (client_name != GSS_C_NO_NAME) gss_release_name(&min_stat, &client_name); - if (token.value) gss_release_buffer(&min_stat, &token); - - return 0; - -cleanup: - glite_gsplugin_free_context(plugin_ctx); - soap_end(soap); - - return ret; -} - -static int request(int conn,struct timeval *to,void *data) -{ - struct soap *soap = data; - glite_jp_context_t ctx = soap->user; - - glite_gsplugin_set_timeout(glite_gsplugin_get_context(soap),to); - - soap->max_keep_alive = 1; /* XXX: prevent gsoap to close connection */ - soap_begin(soap); - if (soap_begin_recv(soap)) { - if (soap->error < SOAP_STOP) { - soap_send_fault(soap); - return EIO; - } - return ENOTCONN; - } - - soap->keep_alive = 1; - if (soap_envelope_begin_in(soap) - || soap_recv_header(soap) - || soap_body_begin_in(soap) - || jpps__serve_request(soap) -#if GSOAP_VERSION >= 20700 - || (soap->fserveloop && soap->fserveloop(soap)) -#endif - ) - { - soap_send_fault(soap); - if (ctx->error) { - /* XXX: shall we die on some errors? */ - int err = ctx->error->code; - glite_jp_clear_error(ctx); - return err; - } - return 0; - } - - glite_jp_run_deferred(ctx); - return 0; -} - -static int reject(int conn) -{ - int flags = fcntl(conn, F_GETFL, 0); - - fcntl(conn,F_SETFL,flags | O_NONBLOCK); - edg_wll_gss_reject(conn); - - return 0; -} - -static int disconn(int conn,struct timeval *to,void *data) -{ - struct soap *soap = (struct soap *) data; - soap_end(soap); // clean up everything and close socket - - return 0; -} - -#define WSPACE "\t\n " - -static int call_opts(glite_jp_context_t ctx,char *opt,char *name,int (*f)(glite_jp_context_t,int,char **)) -{ - int ac = 1,ret,my_optind; - char **av = malloc(sizeof *av),*ap; - - *av = name; - for (ap = strtok(opt,WSPACE); ap; ap = strtok(NULL,WSPACE)) { - av = realloc(av,(ac+1) * sizeof *av); - av[ac++] = ap; - } - - my_optind = optind; - optind = 0; - ret = f(ctx,ac,av); - optind = my_optind; - free(av); - return ret; -} - - -/* XXX: we don't use it */ -SOAP_NMAC struct Namespace namespaces[] = { {NULL,NULL} }; diff --git a/org.glite.jp.primary/src/builtin_plugins.h b/org.glite.jp.primary/src/builtin_plugins.h deleted file mode 100644 index 3b2c201..0000000 --- a/org.glite.jp.primary/src/builtin_plugins.h +++ /dev/null @@ -1,7 +0,0 @@ - -#define GLITE_JP_FILETYPE_TAGS "urn:org.glite.jp.primary:tags" -#define GLITE_JP_FILETYPE_LB "urn:org.glite.jp.primary:lb" -#define GLITE_JP_FILETYPE_ISB "urn:org.glite.jp.primary:isb" -#define GLITE_JP_FILETYPE_OSB "urn:org.glite.jp.primary:osb" - -#define GLITE_JP_FPLUG_TAGS_APPEND 0 diff --git a/org.glite.jp.primary/src/db.h b/org.glite.jp.primary/src/db.h deleted file mode 100644 index 0b9f730..0000000 --- a/org.glite.jp.primary/src/db.h +++ /dev/null @@ -1,83 +0,0 @@ -#ifndef _DB_H -#define _DB_H - -#ident "$Header$" - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef struct _glite_jp_db_stmt_t *glite_jp_db_stmt_t; - -int glite_jp_db_connect( - glite_jp_context_t, /* INOUT: */ - char * /* IN: connect string user/password@host:database */ -); - -void glite_jp_db_close(glite_jp_context_t); - - -/* Parse and execute SQL statement. Returns number of rows selected, created - * or affected by update, or -1 on error */ - -int glite_jp_db_execstmt( - glite_jp_context_t, /* INOUT: */ - char *, /* IN: SQL statement */ - glite_jp_db_stmt_t * /* OUT: statement handle. Usable for - select only */ -); - - -/* Fetch next row of select statement. - * All columns are returned as fresh allocated strings - * - * return values: - * >0 - number of fields of the retrieved row - * 0 - no more rows - * -1 - error - * - * Errors are stored in context passed to previous glite_jp_db_execstmt() */ - -int glite_jp_db_fetchrow( - glite_jp_db_stmt_t, /* IN: statement */ - char ** /* OUT: array of fetched values. - * As number of columns is fixed and known, - * expects allocated array of pointers here */ -); - -/* Retrieve column names of a query statement */ - -int glite_jp_db_querycolumns( - glite_jp_db_stmt_t, /* IN: statement */ - char ** /* OUT: result set column names. Expects allocated array. */ -); - -/* Free the statement structure */ - -void glite_jp_db_freestmt( - glite_jp_db_stmt_t * /* INOUT: statement */ -); - - -/* convert time_t into database-specific time string - * returns pointer to static area that is changed by subsequent calls */ - -char *glite_jp_db_timetodb(time_t); -time_t glite_jp_db_dbtotime(char *); - - -/** - * Check database version. - */ -int glite_jp_db_dbcheckversion(glite_jp_context_t); - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/org.glite.jp.primary/src/feed.c b/org.glite.jp.primary/src/feed.c deleted file mode 100644 index 1c80a8e..0000000 --- a/org.glite.jp.primary/src/feed.c +++ /dev/null @@ -1,336 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/strmd5.h" -#include "feed.h" -#include "file_plugin.h" -#include "builtin_plugins.h" -#include "is_client.h" - -/* - * seconds before feed expires: should be - * XXX: should be configurable, default for real deployment sort of 1 hour - */ -#define FEED_TTL 120 - -static int check_qry_item( - glite_jp_context_t ctx, - const glite_jp_query_rec_t *qry, - const glite_jp_attrval_t *attr -) -{ - int cmp,cmp2; - long scmp,ucmp; - - if (strcmp(qry->attr,attr->name)) return 0; - - if (qry->origin && qry->origin != attr->origin) return 0; - - /* FIXME: fallback only, loop over type plugins and use plugin compare function */ - cmp = strcmp(attr->value,qry->value); - - switch (qry->op) { - case GLITE_JP_QUERYOP_EQUAL: return !cmp; - case GLITE_JP_QUERYOP_UNEQUAL: return cmp; - case GLITE_JP_QUERYOP_LESS: return cmp < 0; - case GLITE_JP_QUERYOP_GREATER: return cmp > 0; - - case GLITE_JP_QUERYOP_WITHIN: - /* FIXME: the same */ - cmp2 = strcmp(attr->value,qry->value); - return cmp >= 0 && cmp2 <= 0; - } -} - -/* XXX: limit on query size -- I'm lazy to malloc() */ -#define QUERY_MAX 100 - -static int match_feed( - glite_jp_context_t ctx, - const struct jpfeed *feed, - const char *job, - -/* XXX: not checked for correctness, - assuming single occurence only */ - const glite_jp_attrval_t attrs[] -) -{ - int i; - int qi[QUERY_MAX]; - - glite_jp_attrval_t *newattr = NULL; - - glite_jp_clear_error(ctx); - - if (feed->qry) { - int j,complete = 1; - - memset(qi,0,sizeof qi); - for (i=0; feed->qry[i].attr; i++) { - int sat = 0; - assert(iqry[i].attr)) { - if (check_qry_item(ctx,feed->qry+i,attrs+j)) { - qi[i] = 1; - sat = 1; /* matched, needn't loop further */ - } - else return 0; /* can't be satisfied either */ - } - - if (!sat) complete = 0; - } - - /* not all attributes in query are known from input - * we have to retrieve job metadata from the backend - * - * XXX: It is not optimal to retrieve it here without sharing - * over multiple invocations of match_feed() for the same job. - */ - if (!complete) { - glite_jp_attrval_t meta[QUERY_MAX+1]; - int qi2[QUERY_MAX]; - - memset(meta,0,sizeof meta); - j=0; - for (i=0; feed->qry[i].attr; i++) if (!qi[i]) { - assert(jqry[i].attr; - qi2[j] = i; - j++; - } - - if (glite_jppsbe_get_job_metadata(ctx,job,meta)) { - glite_jp_error_t err; - memset(&err,0,sizeof err); - err.code = EIO; - err.source = __FUNCTION__; - err.desc = "complete query"; - return glite_jp_stack_error(ctx,&err); - } - - for (i=0; meta[i].name; i++) - if (!check_qry_item(ctx,feed->qry+qi2[i],meta+i)) - return 0; - } - } - - /* matched completely */ - return glite_jpps_single_feed(ctx,feed->destination,job,attrs); - return 0; -} - -int glite_jpps_match_attr( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t attrs[] -) -{ - struct jpfeed *f = (struct jpfeed *) ctx->feeds; - int i,j,doit; - - for (;f; f = f->next) { - doit = 0; - - for (i=0; !doit && f->attrs[i]; i++) - for (j=0; !doit && attrs[j].name; j++) - if (!strcmp(f->attrs[i],attrs[j].name)) doit = 1; - - /* XXX: ignore any errors */ - if (doit) match_feed(ctx,f,job,attrs); - } - - return glite_jp_clear_error(ctx); -} - -static int attr_void_cmp(const void *a, const void *b) -{ - char const * const *ca = (char const * const *) a; - char const * const *cb = (char const * const *) b; - return strcmp(*ca,*cb); -} - -static void attr_union(char **a, char **b, char ***c) -{ - int ca = 0,cb = 0,cnt,i,j; - char **out; - - if (a) for (ca = 0; a[ca]; ca++); - if (b) for (cb = 0; b[cb]; cb++); - out = malloc((ca+cb+1) * sizeof *out); - if (a) memcpy(out,a,ca * sizeof *out); - if (b) memcpy(out+ca,b,cb * sizeof *out); - out[cnt = ca+cb] = NULL; - qsort(out,cnt,sizeof *out,attr_void_cmp); - - for (i=0; i i+1) memmove(out+i+1,out+j,(cnt-j) * sizeof *out); - cnt -= j-i-1; - } - - *c = out; -} - -int glite_jpps_match_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name -) -{ - glite_jpps_fplug_data_t **pd = NULL; - int pi; - void *bh = NULL; - int ret; - struct jpfeed *f = ctx->feeds; - - int nvals = 0,j,i; - char **attrs = NULL, **attrs2; - glite_jp_attrval_t *vals = NULL,*oneval; - - fprintf(stderr,"%s: %s %s %s\n",__FUNCTION__,job,class,name); - - - switch (glite_jpps_fplug_lookup(ctx,class,&pd)) { - case ENOENT: return 0; /* XXX: shall we complain? */ - case 0: break; - default: return -1; - } - - for (;f;f=f->next) { - attr_union(attrs,f->attrs,&attrs2); - free(attrs); - attrs = attrs2; - } - - for (pi=0; pd[pi]; pi++) { - int ci; - for (ci=0; pd[pi]->uris[ci]; ci++) if (!strcmp(pd[pi]->uris[ci],class)) { - void *ph; - - if (!bh && (ret = glite_jppsbe_open_file(ctx,job,pd[pi]->classes[ci],name,O_RDONLY,&bh))) { - free(pd); - return ret; - } - - if (pd[pi]->ops.open(pd[pi]->fpctx,bh,class,&ph)) { - /* XXX: complain more visibly */ - fputs("plugin open failed\n",stderr); - continue; - } - - for (i=0; attrs[i]; i++) - if (!pd[pi]->ops.attr(pd[pi]->fpctx,ph,attrs[i],&oneval)) { - /* XXX: ignore error */ - for (j=0; oneval[j].name; j++); - vals = realloc(vals,(nvals+j+1) * sizeof *vals); - memcpy(vals+nvals,oneval,(j+1) * sizeof *vals); - nvals += j; - free(oneval); - } - - pd[pi]->ops.close(pd[pi]->fpctx,ph); - } - } - - free(attrs); - - for (f = ctx->feeds; f; f=f->next) { - int k; - glite_jp_attrval_t * fattr = malloc((nvals+1) * sizeof *fattr); - - j = 0; - for (i=0; iattrs[k]; k++) - if (!strcmp(f->attrs[k],vals[i].name)) - memcpy(fattr+j++,vals+i,sizeof *fattr); - - memset(fattr+j,0,sizeof *fattr); - glite_jpps_single_feed(ctx,f->destination,job,fattr); - free(fattr); - } - - for (i=0; vals[i].name; i++) glite_jp_attrval_free(vals+i,0); - free(vals); - - if (bh) glite_jppsbe_close_file(ctx,bh); - free(pd); - - return 0; -} - -static char *generate_feedid(void) -{ - char hname[200],buf[1000]; - - gethostname(hname,sizeof hname); - snprintf(buf,sizeof buf,"%s%d%ld",hname,getpid(),lrand48()); - buf[sizeof buf-1] = 0; - return str2md5base64(buf); -} - - -int glite_jpps_run_feed( - glite_jp_context_t ctx, - const char *destination, - char const * const *attrs, - const glite_jp_query_rec_t *qry, - char **feed_id) -{ - fprintf(stderr,"%s: \n",__FUNCTION__); - return 0; -} - -static int register_feed_deferred(glite_jp_context_t ctx,void *feed) -{ - struct jpfeed *f = feed; - - f->next = ctx->feeds; - ctx->feeds = f; - return 0; -} - -/* FIXME: - * - volatile implementation: should store the registrations in a file - * and recover after restart - * - should communicate the data among all server slaves - */ -int glite_jpps_register_feed( - glite_jp_context_t ctx, - const char *destination, - char const *const *attrs, - const glite_jp_query_rec_t *qry, - char **feed_id, - time_t *expires) -{ - int i; - struct jpfeed *f = calloc(1,sizeof *f); - - if (!*feed_id) *feed_id = generate_feedid(); - time(expires); *expires += FEED_TTL; - - f->id = strdup(*feed_id); - f->destination = strdup(destination); - f->expires = *expires; - for (i=0; attrs[i]; i++) { - f->attrs = realloc(f->attrs,(i+2) * sizeof *f->attrs); - f->attrs[i] = strdup(attrs[i]); - f->attrs[i+1] = NULL; - } - for (i=0; qry[i].attr; i++) { - f->qry = realloc(f->qry,(i+2) * sizeof *f->qry); - glite_jp_queryrec_copy(f->qry+i,qry+i); - memset(f->qry+i+1,0,sizeof *f->qry); - } - - glite_jp_add_deferred(ctx,register_feed_deferred,f); - - return 0; -} - diff --git a/org.glite.jp.primary/src/feed.h b/org.glite.jp.primary/src/feed.h deleted file mode 100644 index d141c5b..0000000 --- a/org.glite.jp.primary/src/feed.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef __GLITE_JP_FEED -#define __GLITE_JP_FEED - - -struct jpfeed { - char *id,*destination; - time_t expires; - char **attrs; - glite_jp_query_rec_t *qry; - struct jpfeed *next; -}; - - -int glite_jpps_match_attr(glite_jp_context_t,const char *,const glite_jp_attrval_t[]); -int glite_jpps_match_file(glite_jp_context_t,const char *,const char *,const char *); -int glite_jpps_match_tag(glite_jp_context_t,const char *,const char *,const char *); -int glite_jpps_run_feed(glite_jp_context_t,const char *,char const * const *,const glite_jp_query_rec_t *,char **); -int glite_jpps_register_feed(glite_jp_context_t,const char *,char const * const *,const glite_jp_query_rec_t *,char **,time_t *); - -#endif - diff --git a/org.glite.jp.primary/src/file_plugin.c b/org.glite.jp.primary/src/file_plugin.c deleted file mode 100644 index 144a231..0000000 --- a/org.glite.jp.primary/src/file_plugin.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include "file_plugin.h" - -static struct option opts[] = { - { "plugin", 1, NULL, 'p' }, - { NULL } -}; - -static int loadit(glite_jp_context_t ctx,const char *so) -{ -/* XXX: not stored but we never dlclose() yet */ - void *dl_handle = dlopen(so,RTLD_NOW); - - glite_jp_error_t err; - const char *e; - glite_jpps_fplug_data_t *data,*dp; - int i; - - glite_jpps_fplug_init_t init; - memset(&err,0,sizeof err); - - if (!dl_handle) { - err.source = "dlopen()"; - err.code = EINVAL; - err.desc = dlerror(); - return glite_jp_stack_error(ctx,&err); - } - - dlerror(); - init = dlsym(dl_handle,"init"); - e = dlerror(); - if (e) { - char buf[300]; - snprintf(buf,sizeof buf,"dlsym(\"%s\",\"init\")",so); - buf[299] = 0; - err.source = buf; - err.code = ENOENT; - err.desc = e; - return glite_jp_stack_error(ctx,&err); - } - - data = calloc(1,sizeof *data); - - if (init(ctx,data)) return -1; - - i = 0; - if (ctx->plugins) for (i=0; ctx->plugins[i]; i++); - ctx->plugins = realloc(ctx->plugins, (i+2) * sizeof *ctx->plugins); - ctx->plugins[i] = data; - ctx->plugins[i+1] = NULL; - - /* TODO: check consistency of uri+class pairs wrt. previous plugins */ - - return 0; -} - -int glite_jpps_fplug_load(glite_jp_context_t ctx,int argc,char **argv) -{ - int i; - - for (i=1; iplugins) { - return glite_jp_stack_error(ctx,&err); - } - - for (i = 0; ctx->plugins[i]; i++) { - int j; - glite_jpps_fplug_data_t *p = ctx->plugins[i]; - - for (j=0; p->uris && p->uris[j]; j++) - if (!strcmp(p->uris[j],uri)) { - out = realloc(out, (matches+2) * sizeof *out); - out[matches++] = p; - out[matches] = NULL; - } - } - - if (matches) { - *plugin_data = out; - return 0; - } - else return glite_jp_stack_error(ctx,&err); -} - diff --git a/org.glite.jp.primary/src/ftp_backend.c b/org.glite.jp.primary/src/ftp_backend.c deleted file mode 100644 index 8bf523b..0000000 --- a/org.glite.jp.primary/src/ftp_backend.c +++ /dev/null @@ -1,1744 +0,0 @@ -#ident "$Header$" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" -#include "glite/jp/strmd5.h" - -#include "tags.h" -#include "backend.h" - -#define UPLOAD_SUFFIX ".upload" -#define LOCK_SUFFIX ".lock" - -struct ftpbe_config { - char *internal_path; - char *external_path; - char *gridmap; - char *logname; -}; - -static struct ftpbe_config *config = NULL; - -struct fhandle_rec { - int fd; - int fd_append; -}; -typedef struct fhandle_rec *fhandle; - -static struct option ftpbe_opts[] = { - { "ftp-internal-path", 1, NULL, 'I' }, - { "ftp-external-path", 1, NULL, 'E' }, - { "ftp-gridmap", 1, NULL, 'G' }, - { NULL, 0, NULL, 0 } -}; - -/* obsolete */ -#if 0 -static struct { - glite_jp_fileclass_t type; - char * fname; - } class_to_fname_tab[] = { - { GLITE_JP_FILECLASS_INPUT, "input" }, - { GLITE_JP_FILECLASS_OUTPUT, "output" }, - { GLITE_JP_FILECLASS_LBLOG, "lblog" }, - { GLITE_JP_FILECLASS_TAGS, "tags" }, - { GLITE_JP_FILECLASS_UNDEF, NULL } - }; - -static char *class_to_fname(glite_jp_fileclass_t type) -{ - int i; - - for (i = 0; class_to_fname_tab[i].type != GLITE_JP_FILECLASS_UNDEF; i++) - if (type == class_to_fname_tab[i].type) - return class_to_fname_tab[i].fname; - - return NULL; -} - -static glite_jp_fileclass_t fname_to_class(char* fname) -{ - int i; - - for (i = 0; class_to_fname_tab[i].type != GLITE_JP_FILECLASS_UNDEF; i++) - if (!strcmp(fname, class_to_fname_tab[i].fname)) - return class_to_fname_tab[i].type; - - return GLITE_JP_FILECLASS_UNDEF; -} -#endif - -static int config_check( - glite_jp_context_t ctx, - struct ftpbe_config *config) -{ - return config == NULL || - config->internal_path == NULL || - config->external_path == NULL || - config->gridmap == NULL || - config->logname == NULL; - - /* XXX check reality */ -} - -static int jobid_unique_pathname(glite_jp_context_t ctx, const char *job, - char **unique, char **ju_path, int get_path) -{ - char *p; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - p = strrchr(job, '/'); - if (!p) { - err.code = EINVAL; - err.desc = "Malformed jobid"; - return glite_jp_stack_error(ctx,&err); - } - /* XXX thorough checks */ - if (!(*unique = strdup(p+1))) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - if (get_path) { - if (!(*ju_path = strdup(p+1))) { - free(*unique); - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - *(*ju_path + 10) = '\0'; - } - return 0; -} - -static int mkdirpath(const char* path, int prefixlen) -{ - char *wpath, *p; - int goout, ret; - - wpath = strdup(path); - if (!wpath) { - errno = ENOMEM; - return -1; - } - - p = wpath + prefixlen; - goout = 0; - while (!goout) { - while (*p == '/') p++; - while (*p != '/' && *p != '\0') p++; - goout = (*p == '\0'); - *p = '\0'; - ret = mkdir(wpath, S_IRUSR | S_IWUSR | S_IXUSR); - if (ret < 0 && errno != EEXIST) break; - *p = '/'; - } - free(wpath); - return goout ? 0 : ret; -} - -static long regtime_trunc(long tv_sec) -{ - return tv_sec / (86400*7); -} - -static long regtime_ceil(long tv_sec) -{ - return (tv_sec % (86400*7)) ? tv_sec/(86400*7)+1 : tv_sec/(86400*7) ; -} - -/********************************************************************************/ -int glite_jppsbe_init( - glite_jp_context_t ctx, - int argc, - char *argv[] -) -{ - glite_jp_error_t err; - int opt; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - config = (struct ftpbe_config *) calloc(1, sizeof *config); - if (!config) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - config->logname = getlogin(); - - while ((opt = getopt_long(argc, argv, "I:E:G:", ftpbe_opts, NULL)) != EOF) { - switch (opt) { - case 'I': config->internal_path = optarg; break; - case 'E': config->external_path = optarg; break; - case 'G': config->gridmap = optarg; break; - default: break; - } - } - - if (config_check(ctx, config)) { - err.code = EINVAL; - err.desc = "Invalid FTP backend configuration"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_init_slave( - glite_jp_context_t ctx -) -{ - /* Nothing to do */ -} - -int glite_jppsbe_register_job( - glite_jp_context_t ctx, - const char *job, - const char *owner -) -{ - glite_jp_error_t err; - char *int_dir = NULL; - char *int_fname = NULL; - char *data_dir = NULL; - char *data_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *ownerhash = NULL; - FILE *regfile = NULL; - struct timeval reg_tv; - long reg_tv_trunc; - struct stat statbuf; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job != NULL); - assert(owner != NULL); - - gettimeofday(®_tv, NULL); - reg_tv_trunc = regtime_trunc(reg_tv.tv_sec); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_dir, "%s/regs/%s", - config->internal_path, ju_path) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - if (mkdirpath(int_dir, strlen(config->internal_path)) < 0 && - errno != EEXIST) { - free(int_dir); - err.code = errno; - err.desc = "Cannot mkdir jobs's reg directory"; - return glite_jp_stack_error(ctx,&err); - } - free(int_dir); - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - if (stat(int_fname, &statbuf) < 0) { - if (errno != ENOENT) { - err.code = errno; - err.desc = "Cannot stat jobs's reg info file"; - goto error_out; - } - } else { - err.code = EEXIST; - err.desc = "Job already registered"; - goto error_out; - } - - regfile = fopen(int_fname, "w"); - if (regfile == NULL) { - err.code = errno; - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - - ownerhash = str2md5(owner); /* static buffer */ - - if (fprintf(regfile, "%d %ld.%06ld %s %s %d %s\n", 1, - (long)reg_tv.tv_sec, (long)reg_tv.tv_usec, job, - ownerhash, strlen(owner), owner) < 1 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot write jobs's reg info file"; - goto error_out; - } - if (fclose(regfile) != 0 ) { - err.code = errno; - err.desc = "Cannot close(write) jobs's reg info file"; - goto error_out; - } - - if (asprintf(&data_dir, "%s/data/%s/%d/%s", - config->internal_path, ownerhash, regtime_trunc(reg_tv.tv_sec), ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (asprintf(&data_fname, "%s/_info", data_dir) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (mkdirpath(data_dir, strlen(config->internal_path)) < 0 && - errno != EEXIST) { - err.code = errno; - err.desc = "Cannot mkdir jobs's data directory"; - goto error_out; - } - - if (link(int_fname, data_fname) < 0) { - err.code = errno; - err.desc = "Cannot link job's reg and data info files"; - goto error_out; - } - -error_out: - free(int_fname); - free(data_fname); - if (err.code && data_dir) rmdir(data_dir); - free(data_dir); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int add_to_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - gridmap = fopen(config->gridmap, "a"); - if (!gridmap) { - err.code = errno; - err.desc = "Cannot open gridmap file"; - return glite_jp_stack_error(ctx,&err); - } - if (fprintf(gridmap, "\"%s\" %s\n", dn, config->logname) < 6 || - ferror(gridmap)) { - err.code = EIO; - err.desc = "Cannot write to gridmap file"; - fclose(gridmap); - return glite_jp_stack_error(ctx,&err); - } - fclose(gridmap); - return 0; -} - -static int remove_from_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - char *temp_name = NULL; - FILE *temp_file = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - /* XXX */ - return 0; -} - -int glite_jppsbe_start_upload( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - const char *content_type, - char **destination_out, - time_t *commit_before_inout -) -{ - char *int_fname = NULL; - char *lock_fname = NULL; - FILE *lockfile = NULL; - FILE *regfile = NULL; - char *data_dir = NULL; - char *data_lock = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *peername = NULL; - int info_version; - long reg_time; - char ownerhash[33]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(destination_out!=NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - peername = glite_jp_peer_name(ctx); - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%*ld %*s %s ", &info_version, - ®_time, ownerhash) < 3 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - fclose(regfile); - - /* XXX authorization */ - - if (asprintf(&data_dir, "%s/data/%s/%d/%s", - config->internal_path, ownerhash, regtime_trunc(reg_time), ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(&lock_fname, "%s/%s" LOCK_SUFFIX, - data_dir, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (commit_before_inout != NULL) - *commit_before_inout = (time_t) LONG_MAX; /* XXX no timeout enforced */ - - lockfile = fopen(lock_fname, "w"); - if (lockfile == NULL) { - err.code = errno; - err.desc = "Cannot open uploads's lock file"; - goto error_out; - } - - if (fprintf(lockfile, "%ld %d %s\n", (long)*commit_before_inout, - peername ? peername : 0, - peername ? peername : "") < 1 || ferror(regfile)) { - fclose(lockfile); - err.code = errno; - err.desc = "Cannot write upload's lock file"; - goto error_out; - } - if (fclose(lockfile) != 0 ) { - err.code = errno; - err.desc = "Cannot close(write) upload's lock file"; - goto error_out; - } - - if (asprintf(destination_out, "%s/data/%s/%d/%s/%s" UPLOAD_SUFFIX, - config->external_path, ownerhash, regtime_trunc(reg_time), ju, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (add_to_gridmap(ctx, peername)) { - err.code = EIO; - err.desc = "Cannot add peer DN to ftp server authorization file"; - goto error_out; - } - -error_out: - free(int_fname); - free(data_dir); - if (err.code && data_lock) unlink(data_lock); - free(data_lock); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_commit_upload( - glite_jp_context_t ctx, - const char *destination -) -{ - size_t dest_len; - size_t suff_len; - size_t extp_len; - long commit_before; - int lockpeerlen; - char *lockpeername = NULL; - char *peername = NULL; - char *dest_rw = NULL; - char *dest_rw_suff = NULL; - char *dest_rw_lock = NULL; - FILE *lockfile = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(destination != NULL); - - suff_len = strlen(UPLOAD_SUFFIX); - dest_len = strlen(destination); - extp_len = strlen(config->external_path); - - if (dest_len < suff_len || - strcmp(UPLOAD_SUFFIX, destination + (dest_len - suff_len)) || - strncmp(destination, config->external_path, extp_len)) { - err.code = EINVAL; - err.desc = "Forged destination path"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&dest_rw_suff, "%s%s", config->internal_path, - destination + extp_len) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - dest_rw = strdup(dest_rw_suff); - if (!dest_rw) { - err.code = ENOMEM; - goto error_out; - } - *(dest_rw + (strlen(dest_rw_suff) - suff_len)) = '\0'; - - if (asprintf(&dest_rw_lock, "%s" LOCK_SUFFIX, dest_rw) == -1) { - err.code = ENOMEM; - goto error_out; - } - - lockfile = fopen(dest_rw_lock, "r"); - if (lockfile == NULL) { - err.code = errno; - err.desc = "Cannot open upload's lock file"; - goto error_out; - } - if (fscanf(lockfile, "%ld %d ", &commit_before, &lockpeerlen) < 2 || ferror(lockfile)) { - fclose(lockfile); - err.code = errno; - err.desc = "Cannot read upload's lock file"; - goto error_out; - } - if (lockpeerlen) { - lockpeername = (char*) calloc(1, lockpeerlen+1); - if (!lockpeername) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(lockpeername, lockpeerlen+1, lockfile) == NULL) { - fclose(lockfile); - err.code = errno; - err.desc = "Cannot read upload's lock file"; - goto error_out; - } - } - fclose(lockfile); - - peername = glite_jp_peer_name(ctx); - if (lockpeername && (!peername || strcmp(lockpeername, peername))) { - err.code = EPERM; - err.desc = "Upload started by client of different identity"; - goto error_out; - } - - if (rename(dest_rw_suff, dest_rw) < 0) { - err.code = errno; - err.desc = "Cannot move upload file to the final place"; - goto error_out; - } - - if (unlink(dest_rw_lock) < 0) { - err.code = errno; - err.desc = "Cannot unlink upload's lock file"; - goto error_out; - } - -error_out: - free(dest_rw); - free(dest_rw_suff); - free(dest_rw_lock); - free(peername); - free(lockpeername); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_destination_info( - glite_jp_context_t ctx, - const char *destination, - char **job, - char **class, - char **name -) -{ - size_t dest_len; - size_t suff_len; - size_t extp_len; - char *dest_rw = NULL; - char *dest_rw_suff = NULL; - char *dest_rw_info = NULL; - FILE *infofile = NULL; - char *classname = NULL; - char jobstr[256+1]; - glite_jp_error_t err; - - assert(destination != NULL); - assert(job != NULL); - assert(class != NULL); - assert(name != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - suff_len = strlen(UPLOAD_SUFFIX); - dest_len = strlen(destination); - extp_len = strlen(config->external_path); - - if (dest_len < suff_len || - strcmp(UPLOAD_SUFFIX, destination + (dest_len - suff_len)) || - strncmp(destination, config->external_path, extp_len)) { - err.code = EINVAL; - err.desc = "Forged destination path"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&dest_rw_suff, "%s%s", config->internal_path, - destination + extp_len) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - dest_rw = strdup(dest_rw_suff); - if (!dest_rw) { - err.code = ENOMEM; - goto error_out; - } - *(dest_rw + (strlen(dest_rw_suff) - suff_len)) = '\0'; - - classname = strrchr(dest_rw,'/'); - if (classname == NULL) { - err.code = EINVAL; - err.desc = "Forged destination path"; - goto error_out; - } - *classname++ ='\0'; - *class = strdup(classname); - -/* XXX: do we need similar check? - if (!class == GLITE_JP_FILECLASS_UNDEF) { - err.code = EINVAL; - err.desc = "Forged destination path"; - goto error_out; - } -*/ - - /* TODO: */ - *name = NULL; - - if (asprintf(&dest_rw_info, "%s/_info", dest_rw) == -1) { - err.code = ENOMEM; - goto error_out; - } - - infofile = fopen(dest_rw_info, "r"); - if (infofile == NULL) { - err.code = errno; - err.desc = "Cannot open _info file"; - goto error_out; - } - if (fscanf(infofile, "%*d %*ld.%*ld %256s ", jobstr) < 1 || ferror(infofile)) { - fclose(infofile); - err.code = errno; - err.desc = "Cannot read _info file"; - goto error_out; - } - *job = strdup(jobstr); - fclose(infofile); - -error_out: - free(dest_rw); - free(dest_rw_suff); - free(dest_rw_info); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - - -int glite_jppsbe_get_job_url( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - char **url_out -) -{ - FILE *regfile = NULL; - char *int_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - int info_version; - long reg_time; - char ownerhash[33]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(url_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%*ld %*s %s", &info_version, - ®_time, ownerhash) < 3 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - fclose(regfile); - - if (asprintf(url_out, "%s/data/%s/%d/%s/%s", - config->external_path, ownerhash, regtime_trunc(reg_time), ju, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - free(int_fname); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_fname( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - char **fname_out -) -{ - FILE *regfile = NULL; - char *int_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - int info_version; - long reg_time; - char ownerhash[33]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(fname_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%*ld %*s %s", &info_version, - ®_time, ownerhash) < 3 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - fclose(regfile); - - if (asprintf(fname_out, "%s/data/%s/%d/%s/%s", - config->internal_path, ownerhash, regtime_trunc(reg_time), ju, class) == -1) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - free(int_fname); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_open_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, /* TODO */ - int mode, - void **handle_out -) -{ - fhandle handle = NULL; - char* fname = NULL; - glite_jp_error_t err; - - assert(handle_out != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (get_job_fname(ctx, job, class, name, &fname)) { - err.code = ctx->error->code; - err.desc = "Cannot construct internal filename"; - return glite_jp_stack_error(ctx,&err); - } - - handle = (fhandle) calloc(1,sizeof(*handle)); - if (handle == NULL) { - err.code = ENOMEM; - goto error_out; - } - - handle->fd = open(fname, mode, S_IRUSR | S_IWUSR); - if (handle->fd < 0) { - err.code = errno; - err.desc = "Cannot open requested file"; - free(handle); - goto error_out; - } - handle->fd_append = open(fname, mode | O_APPEND, S_IRUSR | S_IWUSR); - if (handle->fd_append < 0) { - err.code = errno; - err.desc = "Cannot open requested file for append"; - close(handle->fd); - free(handle); - goto error_out; - } - *handle_out = (void*) handle; - -error_out: - free(fname); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_close_file( - glite_jp_context_t ctx, - void *handle -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (close(((fhandle)handle)->fd_append) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor (fd_append)"; - goto error_out; - } - if (close(((fhandle)handle)->fd) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor"; - goto error_out; - } - -error_out: - free(handle); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_pread( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset, - ssize_t *nbytes_ret -) -{ - ssize_t ret; - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if ((ret = pread(((fhandle)handle)->fd, buf, nbytes, offset)) < 0) { - err.code = errno; - err.desc = "Error in pread()"; - return glite_jp_stack_error(ctx,&err); - } - *nbytes_ret = ret; - - return 0; -} - -int glite_jppsbe_pwrite( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (pwrite(((fhandle)handle)->fd, buf, nbytes, offset) < 0) { - err.code = errno; - err.desc = "Error in pwrite()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_append( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (write(((fhandle)handle)->fd_append, buf, nbytes) < 0) { - err.code = errno; - err.desc = "Error in write()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -static int get_job_info( - glite_jp_context_t ctx, - const char *job, - char **owner, - struct timeval *tv_reg -) -{ - char *ju = NULL; - char *ju_path = NULL; - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char *int_fname = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %*s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, &ownerlen) < 4 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - free(int_fname); - free(ju); - free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_info_int( - glite_jp_context_t ctx, - const char *int_fname, - char **jobid, - char **owner, - struct timeval *tv_reg -) -{ - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char jobid_buf[256]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, jobid_buf, &ownerlen) < 5 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - *jobid = strdup(jobid_buf); - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_get_job_metadata( - glite_jp_context_t ctx, - const char *job, - glite_jp_attrval_t attrs_inout[] -) -{ - int got_info = 0; - struct timeval tv_reg; - char *owner = NULL; - int got_tags = 0; - void *tags_handle = NULL; - glite_jp_tagval_t* tags = NULL; - int i,j; - glite_jp_error_t err; - - assert(job != NULL); - assert(attrs_inout != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; attrs_inout[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TIME: -*/ - if (!got_info) { - if (get_job_info(ctx, job, &owner, &tv_reg)) { - err.code = ctx->error->code; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - got_info = 1; - } - break; - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - if (!got_tags) { - if (glite_jppsbe_open_file(ctx, job, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - got_tags = 1; - } - break; -*/ - default: - err.code = EINVAL; - err.desc = "Invalid attribute type"; - goto error_out; - break; - } - - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - attrs_inout[i].value.s = strdup(owner); - if (!attrs_inout[i].value.s) { - err.code = ENOMEM; - err.desc = "Cannot copy owner string"; - goto error_out; - } - break; - case GLITE_JP_ATTR_TIME: - attrs_inout[i].value.time = tv_reg; - break; - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, attrs_inout[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &attrs_inout[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) attrs_inout[i].value.tag.name = NULL; - break; -*/ - default: - break; - } - } - -error_out: - free(owner); - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); - - if (err.code) { - while (i > 0) { - i--; - switch (attrs_inout[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - free(attrs_inout[i].value.s); - break; - case GLITE_JP_ATTR_TAG: - free(attrs_inout[i].value.tag.name); - free(attrs_inout[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} -static int compare_timeval(struct timeval a, struct timeval b) -{ - if (a.tv_sec < b.tv_sec) return -1; - if (a.tv_sec > b.tv_sec) return 1; - if (a.tv_usec < b.tv_usec) return -1; - if (a.tv_usec > b.tv_usec) return 1; - return 0; -} - - -/* FIXME: disabled -- clarification wrt. filetype plugin needed */ - -#if 0 - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -); - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - char *time_dirname = NULL; - DIR *time_dirp = NULL; - struct dirent *jobent; - char *info_fname = NULL; - char *jobid = NULL; - char *owner = NULL; - struct timeval tv_reg; - void *tags_handle = NULL; - int matching; - int i, j; - glite_jp_tagval_t* tags = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (asprintf(&time_dirname, "%s/data/%s/%d", config->internal_path, - ownerhash, regtime_tr) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - time_dirp = opendir(time_dirname); - if (!time_dirp) { - free(time_dirname); - return 0; /* found nothing */ - } - while ((jobent = readdir(time_dirp)) != NULL) { - if (!strcmp(jobent->d_name, ".")) continue; - if (!strcmp(jobent->d_name, "..")) continue; - if (asprintf(&info_fname, "%s/%s/_info", time_dirname, - jobent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (get_job_info_int(ctx, info_fname, &jobid, &owner, &tv_reg)) { - err.code = EIO; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - if (q_tags || md_tags) { - if (glite_jppsbe_open_file(ctx, jobid, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - tags_handle = NULL; - } - - matching = 1; - for (i = 0; matching && query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (query[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - if (query[i].value.s == NULL || - strcmp(query[i].value.s, owner)) matching = 0; - break; - case GLITE_JP_ATTR_TIME: - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_LESS: - matching = compare_timeval(tv_reg, query[i].value.time) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = compare_timeval(tv_reg, query[i].value.time) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = compare_timeval(tv_reg, query[i].value.time) >= 0 - && compare_timeval(tv_reg, query[i].value2.time) <= 0; - break; - } - break; - case GLITE_JP_ATTR_TAG: - if (!tags) { - matching = 0; - break; - } - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, query[i].attr.name)) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_LESS: - matching = strcmp(tags[j].value, query[i].value.s) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = strcmp(tags[j].value, query[i].value.s) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = strcmp(tags[j].value, query[i].value.s) >= 0 \ - && strcmp(tags[j].value, query[i].value2.s) <= 0 ; - break; - default: - break; - } - } - } - break; - default: - break; - } - } - if (!matching) { - free(info_fname); info_fname = NULL; - free(jobid); jobid = NULL; - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); tags = NULL; - continue; - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - metadata[i].value.s = owner; - break; - case GLITE_JP_ATTR_TIME: - metadata[i].value.time = tv_reg; - break; - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, metadata[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &metadata[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) { - metadata[i].value.tag.name = NULL; - metadata[i].value.tag.value = NULL; - } - break; - default: - break; - } - } - (*callback)(ctx, jobid, metadata); - free(jobid); jobid = NULL; - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - } - -error_out: - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - if (tags_handle) glite_jppsbe_close_file(ctx, tags_handle); - free(info_fname); - free(owner); - free(jobid); - closedir(time_dirp); - free(time_dirname); - if (err.code) { - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else - return 0; -} - -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - /* XXX clone metadata */ - int i; - char *q_exact_owner = NULL; - char *ownerhash = NULL; - long q_min_time = 0; - long q_max_time = LONG_MAX; - long q_min_time_tr; - long q_max_time_tr; - int q_with_tags = 0; - int md_info = 0; - int md_tags = 0; - char *owner_dirname = NULL; - DIR *owner_dirp = NULL; - struct dirent *ttimeent; - char *data_dirname = NULL; - DIR *data_dirp = NULL; - struct dirent *ownerent; - long ttime = 0; - glite_jp_attrval_t *metadata_templ = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - if (query[i].attr.type == GLITE_JP_ATTR_OWNER && query[i].op == GLITE_JP_QUERYOP_EQUAL) { - q_exact_owner = query[i].value.s; - } - if (query[i].attr.type == GLITE_JP_ATTR_TIME) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - q_min_time = query[i].value.time.tv_sec; - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_LESS: - if (q_max_time > query[i].value.time.tv_sec + 1) - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_WITHIN: - if (q_max_time > query[i].value2.time.tv_sec + 1) - q_max_time = query[i].value2.time.tv_sec + 1; - /* fallthrough */ - case GLITE_JP_QUERYOP_GREATER: - if (q_min_time < query[i].value.time.tv_sec) - q_min_time = query[i].value.time.tv_sec; - break; - default: - err.code = EINVAL; - err.desc = "Invalid query op"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - if (query[i].attr.type == GLITE_JP_ATTR_TAG) - q_with_tags = 1; - - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TIME: - md_info = 1; - break; - case GLITE_JP_ATTR_TAG: - md_tags = 1; - break; - default: - err.code = EINVAL; - err.desc = "Invalid attribute type in metadata parameter"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - metadata_templ = (glite_jp_attrval_t *) calloc(i + 1, sizeof(glite_jp_attrval_t)); - if (!metadata_templ) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - memcpy(metadata_templ, metadata, (i + 1) * sizeof(glite_jp_attrval_t)); - - q_min_time_tr = regtime_trunc(q_min_time); - q_max_time_tr = regtime_ceil(q_max_time); - - if (q_exact_owner) { - ownerhash = str2md5(q_exact_owner); /* static buffer */ - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, ownerhash) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - free(metadata_templ); - return 0; /* found nothing */ - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerhash, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - } else { /* !q_exact_owner */ - if (asprintf(&data_dirname, "%s/data", config->internal_path) == -1) { - err.code = ENOMEM; - goto error_out; - } - data_dirp = opendir(data_dirname); - if (!data_dirp) { - err.code = EIO; - err.desc = "Cannot open data directory"; - goto error_out; - } - while ((ownerent = readdir(data_dirp)) != NULL) { - if (!strcmp(ownerent->d_name, ".")) continue; - if (!strcmp(ownerent->d_name, "..")) continue; - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, - ownerent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - err.code = EIO; - err.desc = "Cannot open owner data directory"; - goto error_out; - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerent->d_name, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - closedir(owner_dirp); owner_dirp = NULL; - } - closedir(data_dirp); data_dirp = NULL; - } - return 0; - -error_out: - if (owner_dirp) closedir(owner_dirp); - if (data_dirp) closedir(data_dirp); - free(data_dirname); - free(metadata_templ); - return glite_jp_stack_error(ctx,&err); -} - -#else - -/* placeholder instead */ -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - glite_jp_error_t err; - err.code = ENOSYS; - err.desc = "not implemented"; - return glite_jp_stack_error(ctx,&err); -} - -#endif - -/* XXX: -- no primary authorization yet -- no concurrency control yet -- partial success in pwrite,append -- "unique" part of jobid is assumed to be unique across bookkeeping servers -- repository versioning not fully implemented yet -*/ diff --git a/org.glite.jp.primary/src/is_client.c b/org.glite.jp.primary/src/is_client.c deleted file mode 100644 index bb2ae45..0000000 --- a/org.glite.jp.primary/src/is_client.c +++ /dev/null @@ -1,39 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" - -#include "feed.h" -#include "is_client.h" -/* FIXME -#include "jpis_H.h" -#include "jpis_.nsmap" -*/ - -int glite_jpps_single_feed( - glite_jp_context_t ctx, - const char *destination, - const char *job, - glite_jp_attrval_t const *attrs -) -{ - /* TODO: really call JP Index server (via interlogger) */ - printf("feed to %s, job %s\n",destination,job); - -/* FIXME */ -#if 0 - if (soap_call_jpsrv__UpdateJobs(ctx->other_soap,destination,"", - /* FIXME: feedId */ "", - /* FIXME: UpdateJobsData */ NULL, - 0, - NULL - )) fprintf(stderr,"UpdateJobs: %s %s\n",ctx->other_soap->fault->faultcode, - ctx->other_soap->fault->faultstring); - -#endif - return 0; -} diff --git a/org.glite.jp.primary/src/is_client.h b/org.glite.jp.primary/src/is_client.h deleted file mode 100644 index a15d998..0000000 --- a/org.glite.jp.primary/src/is_client.h +++ /dev/null @@ -1 +0,0 @@ -int glite_jpps_single_feed(glite_jp_context_t,const char *,const char *,glite_jp_attrval_t const *); diff --git a/org.glite.jp.primary/src/jptype_map.h b/org.glite.jp.primary/src/jptype_map.h deleted file mode 100644 index c620c83..0000000 --- a/org.glite.jp.primary/src/jptype_map.h +++ /dev/null @@ -1,34 +0,0 @@ -#include "soap_version.h" - -#if GSOAP_VERSION >= 20700 -#define INPUT_SANDBOX jptype__UploadClass__INPUT_SANDBOX -#define OUTPUT_SANDBOX jptype__UploadClass__OUTPUT_SANDBOX -#define JOB_LOG jptype__UploadClass__JOB_LOG - -#define OWNER jptype__AttributeType__OWNER -#define TIME jptype__AttributeType__TIME -#define TAG jptype__AttributeType__TAG - -#define EQUAL jptype__queryOp__EQUAL -#define UNEQUAL jptype__queryOp__UNEQUAL -#define LESS jptype__queryOp__LESS -#define GREATER jptype__queryOp__GREATER -#define WITHIN jptype__queryOp__WITHIN - -#else - -#define __jpsrv__RegisterJob __ns1__RegisterJob -#define __jpsrv__StartUpload __ns1__StartUpload -#define __jpsrv__CommitUpload __ns1__CommitUpload -#define __jpsrv__RecordTag __ns1__RecordTag -#define __jpsrv__FeedIndex __ns1__FeedIndex -#define __jpsrv__FeedIndexRefresh __ns1__FeedIndexRefresh -#define __jpsrv__GetJob __ns1__GetJob - -#define SOAP_TYPE___jpsrv__RegisterJob SOAP_TYPE___ns1__RegisterJob -#define SOAP_TYPE___jpsrv__StartUpload SOAP_TYPE___ns1__StartUpload -#define SOAP_TYPE___jpsrv__CommitUpload SOAP_TYPE___ns1__CommitUpload -#define SOAP_TYPE___jpsrv__GetJob SOAP_TYPE___ns1__GetJob - -#endif - diff --git a/org.glite.jp.primary/src/mysql.c b/org.glite.jp.primary/src/mysql.c deleted file mode 100644 index 0f080ce..0000000 --- a/org.glite.jp.primary/src/mysql.c +++ /dev/null @@ -1,265 +0,0 @@ -#ident "$Header$" - -#include "mysql.h" // MySql header file -#include "mysqld_error.h" -#include "errmsg.h" - -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "db.h" - -#define DEFAULTCS "jpps/@localhost:jpps1" -#define GLITE_JP_LB_MYSQL_VERSION 40018 - -static int my_err(glite_jp_context_t ctx, char *function) -{ - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = function; - err.code = EIO; /* XXX */ - err.desc = mysql_error((MYSQL *) ctx->dbhandle); - return glite_jp_stack_error(ctx,&err); -} - -struct _glite_jp_db_stmt_t { - MYSQL_RES *result; - glite_jp_context_t ctx; -}; - -int glite_jp_db_connect(glite_jp_context_t ctx,char *cs) -{ - char *buf = NULL; - char *host,*user,*pw,*db; - char *slash,*at,*colon; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (!cs) cs = DEFAULTCS; - - if (!(ctx->dbhandle = (void *) mysql_init(NULL))) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - mysql_options(ctx->dbhandle, MYSQL_READ_DEFAULT_FILE, "my"); - - host = user = pw = db = NULL; - - buf = strdup(cs); - slash = strchr(buf,'/'); - at = strrchr(buf,'@'); - colon = strrchr(buf,':'); - - if (!slash || !at || !colon) { - free(buf); - err.code = EINVAL; - err.desc = "Invalid DB connect string"; - return glite_jp_stack_error(ctx,&err); - } - - *slash = *at = *colon = 0; - host = at+1; - user = buf; - pw = slash+1; - db = colon+1; - - if (!mysql_real_connect((MYSQL *) ctx->dbhandle,host,user,pw,db,0,NULL,CLIENT_FOUND_ROWS)) { - free(buf); - return my_err(ctx, __FUNCTION__); - } - - free(buf); - return 0; -} - -void glite_jp_db_close(glite_jp_context_t ctx) -{ - mysql_close((MYSQL *) ctx->dbhandle); - ctx->dbhandle = NULL; -} - -int glite_jp_db_execstmt(glite_jp_context_t ctx,char *txt,glite_jp_db_stmt_t *stmt) -{ - int merr; - int retry_nr = 0; - int do_reconnect = 0; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (stmt) { - *stmt = NULL; - } - - while (retry_nr == 0 || do_reconnect) { - do_reconnect = 0; - if (mysql_query((MYSQL *) ctx->dbhandle,txt)) { - /* error occured */ - switch (merr = mysql_errno((MYSQL *) ctx->dbhandle)) { - case 0: - break; - case ER_DUP_ENTRY: - err.code = EEXIST; - err.desc = mysql_error((MYSQL *) ctx->dbhandle); - glite_jp_stack_error(ctx,&err); - return -1; - break; - case CR_SERVER_LOST: - if (retry_nr <= 0) - do_reconnect = 1; - break; - default: - my_err(ctx, __FUNCTION__); - return -1; - break; - } - } - retry_nr++; - } - - if (stmt) { - *stmt = malloc(sizeof(**stmt)); - if (!*stmt) { - err.code = ENOMEM; - glite_jp_stack_error(ctx,&err); - return -1; - } - memset(*stmt,0,sizeof(**stmt)); - (**stmt).ctx = ctx; - (**stmt).result = mysql_store_result((MYSQL *) ctx->dbhandle); - if (!(**stmt).result) { - if (mysql_errno((MYSQL *) ctx->dbhandle)) { - my_err(ctx, __FUNCTION__); - return -1; - } - } - } else { - MYSQL_RES *r = mysql_store_result((MYSQL *) ctx->dbhandle); - mysql_free_result(r); - } - - return mysql_affected_rows((MYSQL *) ctx->dbhandle); -} - -int glite_jp_db_fetchrow(glite_jp_db_stmt_t stmt,char **res) -{ - MYSQL_ROW row; - glite_jp_context_t ctx = stmt->ctx; - int nr,i; - unsigned long *len; - - glite_jp_clear_error(ctx); - - if (!stmt->result) return 0; - - if (!(row = mysql_fetch_row(stmt->result))) { - if (mysql_errno((MYSQL *) ctx->dbhandle)) { - my_err(ctx, __FUNCTION__); - return -1; - } else return 0; - } - - nr = mysql_num_fields(stmt->result); - len = mysql_fetch_lengths(stmt->result); - for (i=0; iresult))) cols[i++] = f->name; - return i == 0; -} - -void glite_jp_db_freestmt(glite_jp_db_stmt_t *stmt) -{ - if (*stmt) { - if ((**stmt).result) mysql_free_result((**stmt).result); - free(*stmt); - *stmt = NULL; - } -} - - -char *glite_jp_db_timetodb(time_t t) -{ - struct tm *tm = gmtime(&t); - char tbuf[256]; - - /* XXX: the very end of our days */ - if (!tm && t == (time_t) LONG_MAX) return strdup("9999-12-31 23:59:59"); - - sprintf(tbuf,"'%4d-%02d-%02d %02d:%02d:%02d'",tm->tm_year+1900,tm->tm_mon+1, - tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec); - - return strdup(tbuf); -} - -time_t glite_jp_db_dbtotime(char *t) -{ - struct tm tm; - - memset(&tm,0,sizeof(tm)); - setenv("TZ","UTC",1); tzset(); - sscanf(t,"%4d-%02d-%02d %02d:%02d:%02d", - &tm.tm_year,&tm.tm_mon,&tm.tm_mday, - &tm.tm_hour,&tm.tm_min,&tm.tm_sec); - tm.tm_year -= 1900; - tm.tm_mon--; - - return mktime(&tm); -} - -int glite_jp_db_dbcheckversion(glite_jp_context_t ctx) -{ - MYSQL *m = (MYSQL *) ctx->dbhandle; - const char *ver_s = mysql_get_server_info(m); - int major,minor,sub,version; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (!ver_s || 3 != sscanf(ver_s,"%d.%d.%d",&major,&minor,&sub)) { - err.code = EINVAL; - err.desc = "problem checking MySQL version"; - return glite_jp_stack_error(ctx,&err); - } - - version = 10000*major + 100*minor + sub; - - if (version < GLITE_JP_LB_MYSQL_VERSION) { - char msg[300]; - - snprintf(msg,sizeof msg,"Your MySQL version is %d. At least %d required.",version, GLITE_JP_LB_MYSQL_VERSION); - err.code = EINVAL; - err.desc = msg; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} diff --git a/org.glite.jp.primary/src/new_ftp_backend.c b/org.glite.jp.primary/src/new_ftp_backend.c deleted file mode 100644 index c94f20f..0000000 --- a/org.glite.jp.primary/src/new_ftp_backend.c +++ /dev/null @@ -1,1791 +0,0 @@ -#ident "$Header$" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" -#include "glite/jp/strmd5.h" - -#include "tags.h" -#include "backend.h" -#include "db.h" - -#include "jpps_H.h" /* XXX: SOAP_TYPE___jpsrv__GetJob */ - -#include "jptype_map.h" - -#define FTPBE_DEFAULT_DB_CS "jpps/@localhost:jpps" - -struct ftpbe_config { - char *internal_path; - char *external_path; - char *db_cs; -// char *gridmap; - char *logname; -}; - -static struct ftpbe_config *config = NULL; - -struct fhandle_rec { - int fd; - int fd_append; -}; -typedef struct fhandle_rec *fhandle; - -static struct option ftpbe_opts[] = { - { "ftp-internal-path", 1, NULL, 'I' }, - { "ftp-external-path", 1, NULL, 'E' }, - { "ftp-db-cs", 1, NULL, 'D' }, -// { "ftp-gridmap", 1, NULL, 'G' }, - { NULL, 0, NULL, 0 } -}; - -/******************************************************************************* - Internal helpers -*******************************************************************************/ - - -static int config_check( - glite_jp_context_t ctx, - struct ftpbe_config *config) -{ - return config == NULL || - config->internal_path == NULL || - config->external_path == NULL || - config->db_cs == NULL || -// config->gridmap == NULL || - config->logname == NULL; - - /* XXX check reality */ -} - -static int jobid_unique_pathname(glite_jp_context_t ctx, const char *job, - char **unique, char **ju_path, int get_path) -{ - char *p; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - p = strrchr(job, '/'); - if (!p) { - err.code = EINVAL; - err.desc = "Malformed jobid"; - return glite_jp_stack_error(ctx,&err); - } - /* XXX thorough checks */ - if (!(*unique = strdup(p+1))) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - if (get_path) { - if (!(*ju_path = strdup(p+1))) { - free(*unique); - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - *(*ju_path + 10) = '\0'; - } - return 0; -} - -static int mkdirpath(const char* path, int prefixlen) -{ - char *wpath, *p; - int goout, ret; - - wpath = strdup(path); - if (!wpath) { - errno = ENOMEM; - return -1; - } - - p = wpath + prefixlen; - goout = 0; - while (!goout) { - while (*p == '/') p++; - while (*p != '/' && *p != '\0') p++; - goout = (*p == '\0'); - *p = '\0'; - ret = mkdir(wpath, S_IRUSR | S_IWUSR | S_IXUSR); - if (ret < 0 && errno != EEXIST) break; - *p = '/'; - } - free(wpath); - return goout ? 0 : ret; -} - -static int store_user(glite_jp_context_t ctx, const char *userid, const char *subj) -{ - glite_jp_error_t err; - char *stmt; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(userid != NULL); - assert(subj != NULL); - - trio_asprintf(&stmt,"insert into users(userid,cert_subj) " - "values ('%|Ss','%|Ss')",userid,subj); - if (!stmt) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - if (ctx->error->code == EEXIST) - glite_jp_clear_error(ctx); - else { - free(stmt); - err.code = EIO; - err.desc = "DB access failed"; - return glite_jp_stack_error(ctx,&err); - } - } - free(stmt); - - return 0; -} - -static long regtime_trunc(long tv_sec) -{ - return tv_sec / (86400*7); -} - -static long regtime_ceil(long tv_sec) -{ - return (tv_sec % (86400*7)) ? tv_sec/(86400*7)+1 : tv_sec/(86400*7) ; -} - -/******************************************************************************** - Backend calls -********************************************************************************/ -int glite_jppsbe_init( - glite_jp_context_t ctx, - int argc, - char *argv[] -) -{ - glite_jp_error_t err; - int opt; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - config = (struct ftpbe_config *) calloc(1, sizeof *config); - if (!config) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - - config->logname = getlogin(); - - while ((opt = getopt_long(argc, argv, "I:E:" /* G: */, ftpbe_opts, NULL)) != EOF) { - switch (opt) { - case 'I': config->internal_path = optarg; break; - case 'E': config->external_path = optarg; break; - case 'D': config->db_cs = optarg; break; -// case 'G': config->gridmap = optarg; break; - default: break; - } - } - - /* Defaults */ - if (!config->db_cs) config->db_cs = strdup(FTPBE_DEFAULT_DB_CS); - - if (config_check(ctx, config)) { - err.code = EINVAL; - err.desc = "Invalid FTP backend configuration"; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jp_db_connect(ctx, config->db_cs)) { - err.code = EIO; - err.desc = "Cannot access backend's database (during init)"; - return glite_jp_stack_error(ctx,&err); - } else { - glite_jp_db_close(ctx); /* slaves open their own connections */ - } - - return 0; -} - -int glite_jppsbe_init_slave( - glite_jp_context_t ctx -) -{ - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (glite_jp_db_connect(ctx, config->db_cs)) { - err.code = EIO; - err.desc = "Cannot access backend's database"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_register_job( - glite_jp_context_t ctx, - const char *job, - const char *owner -) -{ - glite_jp_error_t err; - char *data_dir = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *ownerhash = NULL; - struct timeval reg_tv; - char *stmt = NULL; - char *dbtime = NULL; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job != NULL); - assert(owner != NULL); - - gettimeofday(®_tv, NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - ownerhash = str2md5(owner); /* static buffer */ - if (store_user(ctx, ownerhash, owner)) { - err.code = EIO; - err.desc = "Cannot store user entry"; - goto error_out; - } - - dbtime = glite_jp_db_timetodb(reg_tv.tv_sec); - if (!dbtime) { - err.code = ENOMEM; - goto error_out; - } - - trio_asprintf(&stmt,"insert into jobs(jobid,dg_jobid,owner,reg_time) " - "values ('%|Ss','%|Ss','%|Ss', %s)", - ju, job, ownerhash, dbtime); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - if (ctx->error->code == EEXIST) { - err.code = EEXIST; - err.desc = "Job already registered"; - } - else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - if (asprintf(&data_dir, "%s/data/%s/%d/%s", - config->internal_path, ownerhash, regtime_trunc(reg_tv.tv_sec), ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (mkdirpath(data_dir, strlen(config->internal_path)) < 0 && - errno != EEXIST) { - err.code = errno; - err.desc = "Cannot mkdir jobs's data directory"; - goto error_out; - } - -error_out: - free(data_dir); - free(stmt); free(dbtime); - free(ju); free(ju_path); - - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -#if 0 -static int add_to_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - gridmap = fopen(config->gridmap, "a"); - if (!gridmap) { - err.code = errno; - err.desc = "Cannot open gridmap file"; - return glite_jp_stack_error(ctx,&err); - } - if (fprintf(gridmap, "\"%s\" %s\n", dn, config->logname) < 6 || - ferror(gridmap)) { - err.code = EIO; - err.desc = "Cannot write to gridmap file"; - fclose(gridmap); - return glite_jp_stack_error(ctx,&err); - } - fclose(gridmap); - return 0; -} - -static int remove_from_gridmap(glite_jp_context_t ctx, const char *dn) -{ - FILE *gridmap = NULL; - char *temp_name = NULL; - FILE *temp_file = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - /* XXX */ - return 0; -} -#endif - -int glite_jppsbe_start_upload( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - const char *content_type, - char **destination_out, - time_t *commit_before_inout -) -{ - char *data_basename = NULL; - char *data_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - char *peername = NULL; - char *peerhash = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[2] = { NULL, NULL }; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(destination_out!=NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - peername = glite_jp_peer_name(ctx); - if (peername == NULL) { - err.code = EINVAL; - err.desc = "Cannot obtain client certificate info"; - goto error_out; - } - - trio_asprintf(&stmt, "select owner, reg_time from jobs" - " where jobid='%|Ss'", ju); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such job registered"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 2) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - - glite_jp_db_freestmt(&db_res); - - /* XXX authorization done in soap_ops.c */ - - /* XXX name length */ - if (asprintf(&data_basename, "%s%s%s", class, - (name != NULL) ? "." : "", - (name != NULL) ? name : "") == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(&data_fname, "%s/data/%s/%d/%s/%s", - config->internal_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (asprintf(destination_out, "%s/data/%s/%d/%s/%s", - config->external_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (commit_before_inout != NULL) - /* XXX no timeout enforced */ - /* XXX: gsoap does not like so much, one year should be enough - *commit_before_inout = (time_t) LONG_MAX; - */ - *commit_before_inout = time(NULL) + 365*24*60*60; - - /* - if (add_to_gridmap(ctx, peername)) { - err.code = EIO; - err.desc = "Cannot add peer DN to ftp server authorization file"; - goto error_out; - } - */ - - peerhash = str2md5(peername); /* static buffer */ - if (store_user(ctx, peerhash, peername)) { - err.code = EIO; - err.desc = "Cannot store upload user entry"; - goto error_out; - } - - free(stmt); stmt = NULL; - trio_asprintf(&stmt,"insert into files" - "(jobid,filename,int_path,ext_url,state,deadline,ul_userid) " - "values ('%|Ss','%|Ss','%|Ss','%|Ss','%|Ss', '%|Ss', '%|Ss')", - ju, data_basename, data_fname, *destination_out, "uploading", - glite_jp_db_timetodb(*commit_before_inout), peerhash); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - if (ctx->error->code == EEXIST) { - err.code = EEXIST; - err.desc = "File already stored or upload in progress"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - -error_out: - free(db_row[0]); free(db_row[1]); - free(stmt); - free(data_basename); - free(data_fname); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_commit_upload( - glite_jp_context_t ctx, - const char *destination -) -{ - char *peername = NULL; - char *peerhash = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[7] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - int i; - - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(destination != NULL); - - trio_asprintf(&stmt, "select * from files where " - "ext_url='%|Ss' and state='uploading'", destination); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such upload in progress"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 7) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - glite_jp_db_freestmt(&db_res); - - peername = glite_jp_peer_name(ctx); - if (peername == NULL) { - err.code = EINVAL; - err.desc = "Cannot obtain client certificate info"; - goto error_out; - } - - peerhash = str2md5(peername); /* static buffer */ - if (strcmp(peerhash, db_row[6])) { - err.code = EPERM; - err.desc = "Upload started by client with different identity"; - goto error_out; - } - - free(stmt); - trio_asprintf(&stmt,"update files set state='committed', deadline=NULL " - "where jobid='%|Ss' and filename='%|Ss'", db_row[0], db_row[1]); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if (glite_jp_db_execstmt(ctx, stmt, NULL) < 0) { - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } -error_out: - for (i=0; i<7; i++) free(db_row[i]); - free(peername); - free(stmt); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_destination_info( - glite_jp_context_t ctx, - const char *destination, - char **job, - char **class, - char **name -) -{ - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[2] = { NULL, NULL}; - int i; - char *cp = NULL; - - char *classname = NULL; - glite_jp_error_t err; - - assert(destination != NULL); - assert(job != NULL); - assert(class != NULL); - assert(name != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - - trio_asprintf(&stmt, "select j.dg_jobid,f.filename from jobs j,files f where " - "f.ext_url='%|Ss' and j.jobid=f.jobid", destination); - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "Invalid destination string"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 2) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - glite_jp_db_freestmt(&db_res); - - *job = strdup(db_row[0]); - - cp = strchr(db_row[1],'.'); - if (!cp) { - *name = NULL; - } else { - *cp++ = '\0'; - *name = strdup(cp); - } - *class = strdup(db_row[1]); - - if (!*job || !*class) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - for (i=0; i<2; i++) free(db_row[i]); - free(stmt); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - - -int glite_jppsbe_get_job_url( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - char **url_out -) -{ - char *data_basename = NULL; - char *data_fname = NULL; - char *ju = NULL; - char *ju_path = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[3] = { NULL, NULL, NULL }; - - long reg_time; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(url_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/ : ""name"; - return glite_jp_stack_error(ctx,&err); - } - - trio_asprintf(&stmt, "select j.owner,reg_time,u.cert_subj from jobs j, users u " - "where j.jobid='%|Ss' and j.owner = u.userid", ju); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such job registered"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - free(stmt); stmt = NULL; - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 3) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - - glite_jp_db_freestmt(&db_res); - - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__GetJobFiles,job,db_row[2])) { - err.code = EPERM; - goto error_out; - } - - /* XXX name length */ - if (asprintf(&data_basename, "%s%s%s", class, - (name != NULL) ? "." : "", - (name != NULL) ? name : "") == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(url_out, "%s/data/%s/%d/%s/%s", - config->external_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - - trio_asprintf(&stmt,"select 'x' from files where jobid='%|Ss' " - "and ext_url = '%|Ss' " - "and state='committed' ",ju,*url_out); - - if ((db_retn = glite_jp_db_execstmt(ctx,stmt,&db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "not uploaded yet"; - } - else { - err.code = EIO; - err.desc = "DB access failed"; - } - /* goto error_out; */ - } - -error_out: - free(db_row[0]); free(db_row[1]); - free(stmt); - free(data_basename); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_fname( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - char **fname_out -) -{ - char *data_basename = NULL; - char *ju = NULL; - char *ju_path = NULL; - - char *stmt = NULL; - glite_jp_db_stmt_t db_res; - int db_retn; - char *db_row[2] = { NULL, NULL }; - - long reg_time; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - assert(job!=NULL); - assert(fname_out != NULL); - - assert(class!=NULL); - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - trio_asprintf(&stmt, "select owner, reg_time from jobs " - "where jobid='%|Ss'", ju); - - if (!stmt) { - err.code = ENOMEM; - goto error_out; - } - - if ((db_retn = glite_jp_db_execstmt(ctx, stmt, &db_res)) <= 0) { - if (db_retn == 0) { - err.code = ENOENT; - err.desc = "No such job registered"; - } else { - err.code = EIO; - err.desc = "DB access failed"; - } - goto error_out; - } - - db_retn = glite_jp_db_fetchrow(db_res, db_row); - if (db_retn != 2) { - glite_jp_db_freestmt(&db_res); - err.code = EIO; - err.desc = "DB access failed"; - goto error_out; - } - - glite_jp_db_freestmt(&db_res); - - /* XXX name length */ - if (asprintf(&data_basename, "%s%s%s", class, - (name != NULL) ? "." : "", (name != NULL) ? name : "") == -1) { - err.code = ENOMEM; - goto error_out; - } - - if (asprintf(fname_out, "%s/data/%s/%d/%s/%s", - config->internal_path, db_row[0], - regtime_trunc(glite_jp_db_dbtotime(db_row[1])), - ju, data_basename) == -1) { - err.code = ENOMEM; - goto error_out; - } - -error_out: - free(db_row[0]); free(db_row[1]); - free(stmt); - free(data_basename); - free(ju); free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - - -int glite_jppsbe_open_file( - glite_jp_context_t ctx, - const char *job, - const char *class, - const char *name, - int mode, - void **handle_out -) -{ - fhandle handle = NULL; - char* fname = NULL; - glite_jp_error_t err; - - assert(handle_out != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (get_job_fname(ctx, job, class, name, &fname)) { - err.code = ctx->error->code; - err.desc = "Cannot construct internal filename"; - return glite_jp_stack_error(ctx,&err); - } - - handle = (fhandle) calloc(1,sizeof(*handle)); - if (handle == NULL) { - err.code = ENOMEM; - goto error_out; - } - - handle->fd = open(fname, mode, S_IRUSR | S_IWUSR); - if (handle->fd < 0) { - err.code = errno; - err.desc = "Cannot open requested file"; - free(handle); - goto error_out; - } - handle->fd_append = open(fname, mode | O_APPEND, S_IRUSR | S_IWUSR); - if (handle->fd_append < 0) { - err.code = errno; - err.desc = "Cannot open requested file for append"; - close(handle->fd); - free(handle); - goto error_out; - } - *handle_out = (void*) handle; - -error_out: - free(fname); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_close_file( - glite_jp_context_t ctx, - void *handle -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (close(((fhandle)handle)->fd_append) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor (fd_append)"; - goto error_out; - } - if (close(((fhandle)handle)->fd) < 0) { - err.code = errno; - err.desc = "Error closing file descriptor"; - goto error_out; - } - -error_out: - free(handle); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_pread( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset, - ssize_t *nbytes_ret -) -{ - ssize_t ret; - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if ((ret = pread(((fhandle)handle)->fd, buf, nbytes, offset)) < 0) { - err.code = errno; - err.desc = "Error in pread()"; - return glite_jp_stack_error(ctx,&err); - } - *nbytes_ret = ret; - - return 0; -} - -int glite_jppsbe_pwrite( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes, - off_t offset -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (pwrite(((fhandle)handle)->fd, buf, nbytes, offset) < 0) { - err.code = errno; - err.desc = "Error in pwrite()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jppsbe_append( - glite_jp_context_t ctx, - void *handle, - void *buf, - size_t nbytes -) -{ - glite_jp_error_t err; - - assert(handle != NULL); - assert(buf != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (write(((fhandle)handle)->fd_append, buf, nbytes) < 0) { - err.code = errno; - err.desc = "Error in write()"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -static int get_job_info( - glite_jp_context_t ctx, - const char *job, - char **owner, - struct timeval *tv_reg -) -{ - char *ju = NULL; - char *ju_path = NULL; - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char *int_fname = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (jobid_unique_pathname(ctx, job, &ju, &ju_path, 1) != 0) { - err.code = ctx->error->code; - err.desc = "Cannot obtain jobid unique path/name"; - return glite_jp_stack_error(ctx,&err); - } - - if (asprintf(&int_fname, "%s/regs/%s/%s.info", - config->internal_path, ju_path, ju) == -1) { - err.code = ENOMEM; - goto error_out; - } - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - if (errno == ENOENT) - err.desc = "Job not registered"; - else - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %*s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, &ownerlen) < 4 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - free(int_fname); - free(ju); - free(ju_path); - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -static int get_job_info_int( - glite_jp_context_t ctx, - const char *int_fname, - char **jobid, - char **owner, - struct timeval *tv_reg -) -{ - FILE *regfile = NULL; - long reg_time_sec; - long reg_time_usec; - int ownerlen = 0; - int info_version; - char jobid_buf[256]; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - regfile = fopen(int_fname, "r"); - if (regfile == NULL) { - err.code = errno; - err.desc = "Cannot open jobs's reg info file"; - goto error_out; - } - if (fscanf(regfile, "%d %ld.%ld %s %*s %d ", &info_version, - ®_time_sec, ®_time_usec, jobid_buf, &ownerlen) < 5 || ferror(regfile)) { - fclose(regfile); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - *jobid = strdup(jobid_buf); - if (ownerlen) { - *owner = (char *) calloc(1, ownerlen+1); - if (!*owner) { - err.code = ENOMEM; - goto error_out; - } - if (fgets(*owner, ownerlen+1, regfile) == NULL) { - fclose(regfile); - free(*owner); - err.code = errno; - err.desc = "Cannot read jobs's reg info file"; - goto error_out; - } - } - fclose(regfile); - - tv_reg->tv_sec = reg_time_sec; - tv_reg->tv_usec = reg_time_usec; - -error_out: - if (err.code) { - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} - -int glite_jppsbe_get_job_metadata( - glite_jp_context_t ctx, - const char *job, - glite_jp_attrval_t attrs_inout[] -) -{ - int got_info = 0; - struct timeval tv_reg; - char *owner = NULL; -/* do in plugin - int got_tags = 0; - void *tags_handle = NULL; - glite_jp_tagval_t* tags = NULL; -*/ - int i,j; - glite_jp_error_t err; - - assert(job != NULL); - assert(attrs_inout != NULL); - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; attrs_inout[i].name; i++) { -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TIME: -*/ - if (!strcmp(attrs_inout[i].name,GLITE_JP_ATTR_OWNER)) { - if (!got_info) { - if (get_job_info(ctx, job, &owner, &tv_reg)) { - err.code = ctx->error->code; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - got_info = 1; - } - } - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - if (!got_tags) { - if (glite_jppsbe_open_file(ctx, job, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - got_tags = 1; - } - break; -*/ - else { - err.code = EINVAL; - err.desc = "Invalid attribute type"; - goto error_out; - break; - } - - if (!strcmp(attrs_inout[i].name,GLITE_JP_ATTR_OWNER)) { - attrs_inout[i].value = strdup(owner); - if (!attrs_inout[i].value) { - err.code = ENOMEM; - err.desc = "Cannot copy owner string"; - goto error_out; - } - attrs_inout[i].origin = GLITE_JP_ATTR_ORIG_SYSTEM; - attrs_inout[i].origin_detail = NULL; - - /* FIXME: we must store job registration time somewhere */ - attrs_inout[i].timestamp = 0; - } - -/* TODO: - case GLITE_JP_ATTR_TIME: - attrs_inout[i].value.time = tv_reg; - break; -*/ - -/* must be implemented via filetype plugin - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, attrs_inout[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &attrs_inout[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) attrs_inout[i].value.tag.name = NULL; - break; -*/ - } - -error_out: - free(owner); -/* plugin - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); -*/ - - if (err.code) { - while (i > 0) { - i--; - glite_jp_attrval_free(attrs_inout+i,0); - } - return glite_jp_stack_error(ctx,&err); - } else { - return 0; - } -} -static int compare_timeval(struct timeval a, struct timeval b) -{ - if (a.tv_sec < b.tv_sec) return -1; - if (a.tv_sec > b.tv_sec) return 1; - if (a.tv_usec < b.tv_usec) return -1; - if (a.tv_usec > b.tv_usec) return 1; - return 0; -} - - -/* FIXME: disabled -- clarification wrt. filetype plugin needed */ - -#if 0 - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -); - -static int query_phase2( - glite_jp_context_t ctx, - const char *ownerhash, - long regtime_tr, - int q_tags, - int md_tags, - const glite_jp_query_rec_t query[], - glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - char *time_dirname = NULL; - DIR *time_dirp = NULL; - struct dirent *jobent; - char *info_fname = NULL; - char *jobid = NULL; - char *owner = NULL; - struct timeval tv_reg; - void *tags_handle = NULL; - int matching; - int i, j; - glite_jp_tagval_t* tags = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - if (asprintf(&time_dirname, "%s/data/%s/%d", config->internal_path, - ownerhash, regtime_tr) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - time_dirp = opendir(time_dirname); - if (!time_dirp) { - free(time_dirname); - return 0; /* found nothing */ - } - while ((jobent = readdir(time_dirp)) != NULL) { - if (!strcmp(jobent->d_name, ".")) continue; - if (!strcmp(jobent->d_name, "..")) continue; - if (asprintf(&info_fname, "%s/%s/_info", time_dirname, - jobent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - if (get_job_info_int(ctx, info_fname, &jobid, &owner, &tv_reg)) { - err.code = EIO; - err.desc = "Cannot retrieve job info"; - goto error_out; - } - if (q_tags || md_tags) { - if (glite_jppsbe_open_file(ctx, jobid, GLITE_JP_FILECLASS_TAGS, - O_RDONLY, &tags_handle)) { - err.code = ctx->error->code; - err.desc = "Cannot open tag file"; - goto error_out; - } - if (glite_jpps_tag_readall(ctx, tags_handle, &tags)) { - err.code = ctx->error->code; - err.desc = "Cannot read tags"; - glite_jppsbe_close_file(ctx, tags_handle); - goto error_out; - } - glite_jppsbe_close_file(ctx, tags_handle); - tags_handle = NULL; - } - - matching = 1; - for (i = 0; matching && query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (query[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - if (query[i].value.s == NULL || - strcmp(query[i].value.s, owner)) matching = 0; - break; - case GLITE_JP_ATTR_TIME: - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = compare_timeval(tv_reg, query[i].value.time); - break; - case GLITE_JP_QUERYOP_LESS: - matching = compare_timeval(tv_reg, query[i].value.time) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = compare_timeval(tv_reg, query[i].value.time) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = compare_timeval(tv_reg, query[i].value.time) >= 0 - && compare_timeval(tv_reg, query[i].value2.time) <= 0; - break; - } - break; - case GLITE_JP_ATTR_TAG: - if (!tags) { - matching = 0; - break; - } - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, query[i].attr.name)) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - matching = !strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_UNEQUAL: - matching = strcmp(tags[j].value, query[i].value.s); - break; - case GLITE_JP_QUERYOP_LESS: - matching = strcmp(tags[j].value, query[i].value.s) < 0; - break; - case GLITE_JP_QUERYOP_GREATER: - matching = strcmp(tags[j].value, query[i].value.s) > 0; - break; - case GLITE_JP_QUERYOP_WITHIN: - matching = strcmp(tags[j].value, query[i].value.s) >= 0 \ - && strcmp(tags[j].value, query[i].value2.s) <= 0 ; - break; - default: - break; - } - } - } - break; - default: - break; - } - } - if (!matching) { - free(info_fname); info_fname = NULL; - free(jobid); jobid = NULL; - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - free(tags); tags = NULL; - continue; - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - metadata[i].value.s = owner; - break; - case GLITE_JP_ATTR_TIME: - metadata[i].value.time = tv_reg; - break; - case GLITE_JP_ATTR_TAG: - for (j = 0; tags[j].name != NULL; j++) { - if (!strcmp(tags[j].name, metadata[i].attr.name)) { - if (glite_jpps_tagval_copy(ctx, &tags[j], - &metadata[i].value.tag)) { - err.code = ENOMEM; - err.desc = "Cannot copy tag value"; - goto error_out; - } - break; - } - } - if (!tags[j].name) { - metadata[i].value.tag.name = NULL; - metadata[i].value.tag.value = NULL; - } - break; - default: - break; - } - } - (*callback)(ctx, jobid, metadata); - free(jobid); jobid = NULL; - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - } - -error_out: - if (tags) for (j = 0; tags[j].name != NULL; j++) { - free(tags[j].name); - free(tags[j].value); - } - if (tags_handle) glite_jppsbe_close_file(ctx, tags_handle); - free(info_fname); - free(owner); - free(jobid); - closedir(time_dirp); - free(time_dirname); - if (err.code) { - while (i > 0) { - i--; - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_TAG: - free(metadata[i].value.tag.name); - free(metadata[i].value.tag.value); - default: - break; - } - } - return glite_jp_stack_error(ctx,&err); - } else - return 0; -} - -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - /* XXX clone metadata */ - int i; - char *q_exact_owner = NULL; - char *ownerhash = NULL; - long q_min_time = 0; - long q_max_time = LONG_MAX; - long q_min_time_tr; - long q_max_time_tr; - int q_with_tags = 0; - int md_info = 0; - int md_tags = 0; - char *owner_dirname = NULL; - DIR *owner_dirp = NULL; - struct dirent *ttimeent; - char *data_dirname = NULL; - DIR *data_dirp = NULL; - struct dirent *ownerent; - long ttime = 0; - glite_jp_attrval_t *metadata_templ = NULL; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - for (i = 0; query[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - if (query[i].attr.type == GLITE_JP_ATTR_OWNER && query[i].op == GLITE_JP_QUERYOP_EQUAL) { - q_exact_owner = query[i].value.s; - } - if (query[i].attr.type == GLITE_JP_ATTR_TIME) { - switch (query[i].op) { - case GLITE_JP_QUERYOP_EQUAL: - q_min_time = query[i].value.time.tv_sec; - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_LESS: - if (q_max_time > query[i].value.time.tv_sec + 1) - q_max_time = query[i].value.time.tv_sec + 1; - break; - case GLITE_JP_QUERYOP_WITHIN: - if (q_max_time > query[i].value2.time.tv_sec + 1) - q_max_time = query[i].value2.time.tv_sec + 1; - /* fallthrough */ - case GLITE_JP_QUERYOP_GREATER: - if (q_min_time < query[i].value.time.tv_sec) - q_min_time = query[i].value.time.tv_sec; - break; - default: - err.code = EINVAL; - err.desc = "Invalid query op"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - if (query[i].attr.type == GLITE_JP_ATTR_TAG) - q_with_tags = 1; - - } - - for (i = 0; metadata[i].attr.type != GLITE_JP_ATTR_UNDEF; i++) { - switch (metadata[i].attr.type) { - case GLITE_JP_ATTR_OWNER: - case GLITE_JP_ATTR_TIME: - md_info = 1; - break; - case GLITE_JP_ATTR_TAG: - md_tags = 1; - break; - default: - err.code = EINVAL; - err.desc = "Invalid attribute type in metadata parameter"; - return glite_jp_stack_error(ctx,&err); - break; - } - } - metadata_templ = (glite_jp_attrval_t *) calloc(i + 1, sizeof(glite_jp_attrval_t)); - if (!metadata_templ) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - memcpy(metadata_templ, metadata, (i + 1) * sizeof(glite_jp_attrval_t)); - - q_min_time_tr = regtime_trunc(q_min_time); - q_max_time_tr = regtime_ceil(q_max_time); - - if (q_exact_owner) { - ownerhash = str2md5(q_exact_owner); /* static buffer */ - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, ownerhash) == -1) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - free(metadata_templ); - return 0; /* found nothing */ - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerhash, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - } else { /* !q_exact_owner */ - if (asprintf(&data_dirname, "%s/data", config->internal_path) == -1) { - err.code = ENOMEM; - goto error_out; - } - data_dirp = opendir(data_dirname); - if (!data_dirp) { - err.code = EIO; - err.desc = "Cannot open data directory"; - goto error_out; - } - while ((ownerent = readdir(data_dirp)) != NULL) { - if (!strcmp(ownerent->d_name, ".")) continue; - if (!strcmp(ownerent->d_name, "..")) continue; - if (asprintf(&owner_dirname, "%s/data/%s", config->internal_path, - ownerent->d_name) == -1) { - err.code = ENOMEM; - goto error_out; - } - owner_dirp = opendir(owner_dirname); - free(owner_dirname); - if (!owner_dirp) { - err.code = EIO; - err.desc = "Cannot open owner data directory"; - goto error_out; - } - while ((ttimeent = readdir(owner_dirp)) != NULL) { - if (!strcmp(ttimeent->d_name, ".")) continue; - if (!strcmp(ttimeent->d_name, "..")) continue; - ttime = atol(ttimeent->d_name); - if (ttime >= q_min_time_tr && ttime < q_max_time_tr) { - if (query_phase2(ctx, ownerent->d_name, ttime, q_with_tags, md_tags, - query, metadata_templ, callback)) { - err.code = EIO; - err.desc = "query_phase2() error"; - goto error_out; - } - } - } - closedir(owner_dirp); owner_dirp = NULL; - } - closedir(data_dirp); data_dirp = NULL; - } - return 0; - -error_out: - if (owner_dirp) closedir(owner_dirp); - if (data_dirp) closedir(data_dirp); - free(data_dirname); - free(metadata_templ); - return glite_jp_stack_error(ctx,&err); -} - -#else - -/* placeholder instead */ -int glite_jppsbe_query( - glite_jp_context_t ctx, - const glite_jp_query_rec_t query[], - const glite_jp_attrval_t metadata[], - int (*callback)( - glite_jp_context_t ctx, - const char *job, - const glite_jp_attrval_t metadata[] - ) -) -{ - glite_jp_error_t err; - err.code = ENOSYS; - err.desc = "not implemented"; - return glite_jp_stack_error(ctx,&err); -} - -#endif - -/* XXX: -- no primary authorization yet -- no concurrency control yet -- partial success in pwrite,append -- "unique" part of jobid is assumed to be unique across bookkeeping servers -- repository versioning not fully implemented yet -*/ diff --git a/org.glite.jp.primary/src/simple_server.c b/org.glite.jp.primary/src/simple_server.c deleted file mode 100644 index 3bbb743..0000000 --- a/org.glite.jp.primary/src/simple_server.c +++ /dev/null @@ -1,59 +0,0 @@ -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "jpps_H.h" - -extern SOAP_NMAC struct Namespace jpis__namespaces[],jpps__namespaces[]; - -int main(int argc, char *argv[]) { - struct soap soap; - int i, m, s; // master and slave sockets - - glite_jp_context_t ctx; - - soap_init(&soap); - soap_set_namespaces(&soap, jpps__namespaces); - - glite_jp_init_context(&ctx); - - if (glite_jppsbe_init(ctx, &argc, argv)) { - /* XXX log */ - fputs(glite_jp_error_chain(ctx), stderr); - exit(1); - } - - soap.user = (void *) ctx; - - ctx->other_soap = soap_new(); - soap_init(ctx->other_soap); - soap_set_namespaces(ctx->other_soap,jpis__namespaces); - - srand48(time(NULL)); /* feed id generation */ - - m = soap_bind(&soap, NULL, 8901, 100); - if (m < 0) - soap_print_fault(&soap, stderr); - else - { - fprintf(stderr, "Socket connection successful: master socket = %d\n", m); - for (i = 1; ; i++) { - s = soap_accept(&soap); - if (s < 0) { - soap_print_fault(&soap, stderr); - break; - } - jpps__serve(&soap); // process RPC request - soap_destroy(&soap); // clean up class instances - soap_end(&soap); // clean up everything and close socket - glite_jp_run_deferred(ctx); - } - } - soap_done(&soap); // close master socket - - return 0; -} - -/* XXX: we don't use it */ -SOAP_NMAC struct Namespace namespaces[] = { {NULL,NULL} }; diff --git a/org.glite.jp.primary/src/soap_ops.c b/org.glite.jp.primary/src/soap_ops.c deleted file mode 100644 index d49d112..0000000 --- a/org.glite.jp.primary/src/soap_ops.c +++ /dev/null @@ -1,415 +0,0 @@ -#include -#include -#include - -#include "glite/jp/types.h" -#include "glite/jp/context.h" - -#include "feed.h" - -#include "jpps_H.h" -/* #include "JobProvenancePS.nsmap" */ -#include "jpps_.nsmap" - -#include "jptype_map.h" - -#include "file_plugin.h" -#include "builtin_plugins.h" - -static struct jptype__genericFault *jp2s_error(struct soap *soap, - const glite_jp_error_t *err) -{ - struct jptype__genericFault *ret = NULL; - if (err) { - ret = soap_malloc(soap,sizeof *ret); - memset(ret,0,sizeof *ret); - ret->code = err->code; - ret->source = soap_strdup(soap,err->source); - ret->text = soap_strdup(soap,strerror(err->code)); - ret->description = soap_strdup(soap,err->desc); - ret->reason = jp2s_error(soap,err->reason); - } - return ret; -} - -static void err2fault(const glite_jp_context_t ctx,struct soap *soap) -{ - char *et; - struct SOAP_ENV__Detail *detail = soap_malloc(soap,sizeof *detail); - struct _genericFault *f = soap_malloc(soap,sizeof *f); - - - f->jpelem__genericFault = jp2s_error(soap,ctx->error); - - detail->__type = SOAP_TYPE__genericFault; -#if GSOAP_VERSION >= 20700 - detail->fault = f; -#else - detail->value = f; -#endif - detail->__any = NULL; - - soap_receiver_fault(soap,"Oh, shit!",NULL); - if (soap->version == 2) soap->fault->SOAP_ENV__Detail = detail; - else soap->fault->detail = detail; -} - -#define CONTEXT_FROM_SOAP(soap,ctx) glite_jp_context_t ctx = (glite_jp_context_t) ((soap)->user) - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__RegisterJob( - struct soap *soap, - struct _jpelem__RegisterJob *in, - struct _jpelem__RegisterJobResponse *empty) -{ - CONTEXT_FROM_SOAP(soap,ctx); - glite_jp_attrval_t owner_val[2]; - - printf("%s %s %s\n",__FUNCTION__,in->job,in->owner); - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__RegisterJob,in->job,in->owner) || - glite_jppsbe_register_job(ctx,in->job,in->owner)) - { - err2fault(ctx,soap); - return SOAP_FAULT; - } - - owner_val[0].name = GLITE_JP_ATTR_OWNER; - owner_val[0].value = in->owner; - owner_val[0].origin = GLITE_JP_ATTR_ORIG_SYSTEM; - owner_val[0].timestamp = time(NULL); - owner_val[0].origin_detail = NULL; - owner_val[1].name = NULL; - -/* XXX: errrors should be ingored but not silently */ - glite_jpps_match_attr(ctx,in->job,owner_val); - - return SOAP_OK; -} - - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__StartUpload( - struct soap *soap, - struct _jpelem__StartUpload *in, - struct _jpelem__StartUploadResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - char *destination; - time_t commit_before = in->commitBefore; - glite_jp_error_t err; - glite_jpps_fplug_data_t **pd = NULL; - int i; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__StartUpload,NULL,NULL)) { - err2fault(ctx,soap); - return SOAP_FAULT; - } - - switch (glite_jpps_fplug_lookup(ctx,in->class_,&pd)) { - case ENOENT: - err.code = ENOENT; - err.source = __FUNCTION__; - err.desc = "unknown file class"; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - return SOAP_FAULT; - case 0: break; - default: - err2fault(ctx,soap); - return SOAP_FAULT; - } - - for (i=0; pd[0]->uris[i] && strcmp(pd[0]->uris[i],in->class_); i++); - assert(pd[0]->uris[i]); - - if (glite_jppsbe_start_upload(ctx,in->job,pd[0]->classes[i],in->name,in->contentType, - &destination,&commit_before)) - { - err2fault(ctx,soap); - free(pd); - return SOAP_FAULT; - } - - out->destination = soap_strdup(soap,destination); - free(destination); - out->commitBefore = commit_before; - - free(pd); - return SOAP_OK; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__CommitUpload( - struct soap *soap, - struct _jpelem__CommitUpload *in, - struct _jpelem__CommitUploadResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - char *job,*class,*name; - - job = class = name = NULL; - - if (glite_jpps_authz(ctx,SOAP_TYPE___jpsrv__CommitUpload,NULL,NULL) || - glite_jppsbe_commit_upload(ctx,in->destination)) - { - err2fault(ctx,soap); - return SOAP_FAULT; - } - - /* XXX: should not fail when commit_upload was OK */ - assert(glite_jppsbe_destination_info(ctx,in->destination,&job,&class,&name) == 0); - - /* XXX: ignore errors but don't fail silenty */ - glite_jpps_match_file(ctx,job,class,name); - - free(job); free(class); free(name); - - return SOAP_OK; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__RecordTag( - struct soap *soap, - struct _jpelem__RecordTag *in, - struct _jpelem__RecordTagResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - void *file_be,*file_p; - glite_jpps_fplug_data_t **pd = NULL; - glite_jp_attrval_t attr[2]; - - file_be = file_p = NULL; - - /* XXX: we assume just one plugin and also that TAGS plugin handles - * just one uri/class */ - - if (glite_jpps_fplug_lookup(ctx,GLITE_JP_FILETYPE_TAGS,&pd) - || glite_jppsbe_open_file(ctx,in->jobid,pd[0]->classes[0],NULL, - O_RDWR|O_CREAT,&file_be) - /* XXX: tags need reading to check magic number */ - ) { - free(pd); - err2fault(ctx,soap); - return SOAP_FAULT; - } - - /* XXX: assuming tag plugin handles just one type */ - if (pd[0]->ops.open(pd[0]->fpctx,file_be,GLITE_JP_FILETYPE_TAGS,&file_p) - || pd[0]->ops.generic(pd[0]->fpctx,file_p,GLITE_JP_FPLUG_TAGS_APPEND,in->tag->name,in->tag->value)) - { - err2fault(ctx,soap); - if (file_p) pd[0]->ops.close(pd[0]->fpctx,file_p); - glite_jppsbe_close_file(ctx,file_be); - free(pd); - return SOAP_FAULT; - } - - if (pd[0]->ops.close(pd[0]->fpctx,file_p) - || glite_jppsbe_close_file(ctx,file_be)) - { - err2fault(ctx,soap); - free(pd); - return SOAP_FAULT; - } - - attr[0].name = in->tag->name; - attr[0].value = in->tag->value; - attr[0].origin = GLITE_JP_ATTR_ORIG_USER; - attr[0].timestamp = time(NULL); - attr[0].origin_detail = NULL; /* XXX */ - - /* XXX: ignore errors but don't fail silenty */ - glite_jpps_match_attr(ctx,in->jobid,attr); - - free(pd); - return SOAP_OK; -} - -static void s2jp_qval(const struct jptype__stringOrBlob *in, char **value, int *binary, size_t *size) -{ - if (in->string) { - *value = in->string; - *binary = 0; - *size = 0; - } - else { - assert(in->blob); /* XXX: should report error instead */ - *value = in->blob->__ptr; - *binary = 1; - *size = in->blob->__size; - } -} - -static void s2jp_query(const struct jptype__primaryQuery *in, glite_jp_query_rec_t *out) -{ - int b; - - out->attr = in->attr; - - s2jp_qval(in->value,&out->value,&out->binary,&out->size); - switch (in->op) { - case EQUAL: out->op = GLITE_JP_QUERYOP_EQUAL; break; - case UNEQUAL: out->op = GLITE_JP_QUERYOP_UNEQUAL; break; - case LESS: out->op = GLITE_JP_QUERYOP_LESS; break; - case GREATER: out->op = GLITE_JP_QUERYOP_GREATER; break; - case WITHIN: - out->op = GLITE_JP_QUERYOP_WITHIN; - s2jp_qval(in->value2,&out->value2,&b,&out->size2); - assert(out->binary == b); /* XXX: report error instead */ - - break; - } - - if (in->origin) switch (*in->origin) { - case jptype__attrOrig__SYSTEM: out->origin = GLITE_JP_ATTR_ORIG_SYSTEM; break; - case jptype__attrOrig__USER: out->origin = GLITE_JP_ATTR_ORIG_USER; break; - case jptype__attrOrig__FILE_: out->origin = GLITE_JP_ATTR_ORIG_FILE; break; - } - else out->origin = GLITE_JP_ATTR_ORIG_ANY; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__FeedIndex( - struct soap *soap, - struct _jpelem__FeedIndex *in, - struct _jpelem__FeedIndexResponse *out) -{ - -/* deferred processing: return feed_id to the index server first, - * start feeding it afterwards -- not before the index server actually - * knows feed_id and is ready to accept the feed. - * - * Has to be done within the same server slave, - * passed through the context */ - - CONTEXT_FROM_SOAP(soap,ctx); - char *feed_id = NULL; - time_t expires = 0; - int ret = SOAP_OK; - - char const **attrs = calloc(in->__sizeattributes+1,sizeof *attrs); - glite_jp_query_rec_t *qry = calloc(in->__sizeconditions+1,sizeof *qry); - int i; - - glite_jp_clear_error(ctx); - - memcpy(attrs,in->attributes,sizeof *attrs * in->__sizeattributes); - for (i = 0; i__sizeconditions; i++) s2jp_query(in->conditions[i],qry+i); - - if (in->history) { - if (glite_jpps_run_feed(ctx,in->destination,attrs,qry,&feed_id)) { - err2fault(ctx,soap); - ret = SOAP_FAULT; - goto cleanup; - } - } - - if (in->continuous) { - if (glite_jpps_register_feed(ctx,in->destination,attrs,qry,&feed_id,&expires)) { - err2fault(ctx,soap); - ret = SOAP_FAULT; - goto cleanup; - } - } - - if (!in->history && !in->continuous) { - glite_jp_error_t err; - memset(&err,0,sizeof err); - err.code = EINVAL; - err.source = __FUNCTION__; - err.desc = "at least one of and must be true"; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - ret = SOAP_FAULT; - goto cleanup; - } - - out->feedExpires = expires; - out->feedId = soap_strdup(soap,feed_id); - -cleanup: - free(feed_id); - free(attrs); - free(qry); - - return ret; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__FeedIndexRefresh( - struct soap *soap, - struct _jpelem__FeedIndexRefresh *in, - struct _jpelem__FeedIndexRefreshResponse *out) -{ - fprintf(stderr,"%s: not implemented\n",__FUNCTION__); - abort(); -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__GetJobFiles( - struct soap *soap, - struct _jpelem__GetJobFiles *in, - struct _jpelem__GetJobFilesResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - char *url; - - int i,n; - glite_jp_error_t err; - void **pd; - struct jptype__jppsFile **f = NULL; - - memset(&err,0,sizeof err); - out->__sizefiles = 0; - - for (pd = ctx->plugins; *pd; pd++) { - glite_jpps_fplug_data_t *plugin = *pd; - - for (i=0; plugin->uris[i]; i++) { - glite_jp_clear_error(ctx); - switch (glite_jppsbe_get_job_url(ctx,in->jobid,plugin->classes[i],NULL,&url)) { - case 0: n = out->__sizefiles++; - f = realloc(f,out->__sizefiles * sizeof *f); - f[n] = soap_malloc(soap, sizeof **f); - f[n]->class_ = soap_strdup(soap,plugin->uris[i]); - f[n]->name = NULL; - f[n]->url = soap_strdup(soap,url); - free(url); - break; - case ENOENT: - break; - default: - err.code = ctx->error->code; - err.source = "jpsrv__GetJob()"; - err.desc = plugin->uris[i]; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); - glite_jp_clear_error(ctx); - return SOAP_FAULT; - } - } - } - - if (!out->__sizefiles) { - glite_jp_clear_error(ctx); - err.code = ENOENT; - err.source = __FUNCTION__; - err.desc = "No file found for this job"; - glite_jp_stack_error(ctx,&err); - err2fault(ctx,soap); -// glite_jp_clear_error(ctx); - return SOAP_FAULT; - } - - out->files = soap_malloc(soap,out->__sizefiles * sizeof *f); - memcpy(out->files,f,out->__sizefiles * sizeof *f); - - return SOAP_OK; -} - -SOAP_FMAC5 int SOAP_FMAC6 __jpsrv__GetJobAttributes( - struct soap *soap, - struct _jpelem__GetJobAttributes *in, - struct _jpelem__GetJobAttributesResponse *out) -{ - CONTEXT_FROM_SOAP(soap,ctx); - - /* TODO */ - abort(); -} diff --git a/org.glite.jp.primary/src/tags.c b/org.glite.jp.primary/src/tags.c deleted file mode 100644 index 1f11b4d..0000000 --- a/org.glite.jp.primary/src/tags.c +++ /dev/null @@ -1,233 +0,0 @@ -#include -#include -#include -#include -#include - -#include -#include "tags.h" -#include "backend.h" - -/* magic name_len value_len binary sequence timestamp */ -#define HEADER "JP#TAG# %05u %012lu %c %05u %012lu#" -#define HEADER_SIZE 48 - -int glite_jpps_tag_append( - glite_jp_context_t ctx, - void *handle, - const glite_jp_tagval_t *tag -) -{ - char hdr[HEADER_SIZE+1]; - glite_jp_error_t err; - - unsigned long vlen = tag->binary ? tag->size : - (tag->value ? strlen(tag->value) : 0); - int nlen; - - memset(&err,0,sizeof err); - err.source = "glite_jpps_tag_append()"; - - if (!tag->name) { - err.code = EINVAL; - err.desc = "tag name"; - return glite_jp_stack_error(ctx,&err); - } - - nlen = strlen(tag->name); - - assert(sprintf(hdr,HEADER,nlen,vlen, - tag->binary ? "B" : "S", - tag->sequence, tag->timestamp) == HEADER_SIZE); - - if (glite_jppsbe_append(ctx,handle,hdr,HEADER_SIZE)) { - err.code = EIO; - err.desc = "write tag header"; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jppsbe_append(ctx,handle,tag->name,nlen)) { - err.code = EIO; - err.desc = "write tag name"; - return glite_jp_stack_error(ctx,&err); - } - - if (glite_jppsbe_append(ctx,handle,tag->value,vlen)) { - err.code = EIO; - err.desc = "write tag value"; - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} - -int glite_jpps_tagval_copy( - glite_jp_context_t ctx, - glite_jp_tagval_t *from, - glite_jp_tagval_t *to -) -{ - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - to->name = strdup(from->name); - if (!to->name) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - to->sequence = from->sequence; - to->timestamp = from->timestamp; - to->binary = from->binary; - to->size = from->size; - to->value = (char *) malloc(to->size); - if (!to->value) { - free(to->name); - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - memcpy(from->value, to->value, to->size); - - return 0; -} - -int glite_jpps_tag_read( - glite_jp_context_t ctx, - void *handle, - off_t offset, - glite_jp_tagval_t *tagvalue, - size_t *shift -) -{ - char hdr[HEADER_SIZE+1]; - unsigned int nlen; - unsigned long vlen; - char binary; - unsigned sequence; - unsigned timestamp; - char * name = NULL; - char * value = NULL; - ssize_t ret; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - hdr[HEADER_SIZE] = '\0'; - if (glite_jppsbe_pread(ctx, handle, hdr, HEADER_SIZE, offset, &ret)) { - err.code = EIO; - err.desc = "Cannot read tag header"; - goto error_out; - } - if (ret == 0) { - err.code = ENOENT; - err.desc = "No more tags in the file"; - goto error_out; - } - /* #define HEADER "JP#TAG# %05u %012lu %c %05u %012lu#" */ - if (sscanf(hdr, HEADER, &nlen, &vlen, &binary, &sequence, ×tamp) < 5) { - err.code = EILSEQ; - err.desc = "Incorrect tag header format"; - goto error_out; - } - name = (char*) malloc(nlen + 1); - if (!name) { - err.code = ENOMEM; - goto error_out; - } - name[nlen] = '\0'; - value = (char*) malloc(vlen + 1); - if (!value) { - err.code = ENOMEM; - goto error_out; - } - value[vlen] = '\0'; - if (glite_jppsbe_pread(ctx, handle, name, nlen, offset + HEADER_SIZE, &ret)) { - err.code = EIO; - err.desc = "Cannot read tag name"; - goto error_out; - } - if (glite_jppsbe_pread(ctx, handle, value, vlen, offset + HEADER_SIZE + nlen, &ret)) { - err.code = EIO; - err.desc = "Cannot read tag value"; - goto error_out; - } - - tagvalue->name = name; - tagvalue->sequence = sequence; - tagvalue->timestamp = timestamp; - tagvalue->binary = (binary == 'B') ? 1 : 0; - tagvalue->size = vlen; - tagvalue->value = value; - - *shift = HEADER_SIZE + nlen + vlen; - - return 0; -error_out: - free(name); - free(value); - return glite_jp_stack_error(ctx,&err); -} - -/* -int glite_jpps_tag_read(glite_jp_context_t, void *, off_t, glite_jp_tagval_t *, size_t); -int glite_jpps_tag_readall(glite_jp_context_t, void *, glite_jp_tagval_t **); -*/ - -int glite_jpps_tag_readall( - glite_jp_context_t ctx, - void *handle, - glite_jp_tagval_t **tags_out -) -{ - glite_jp_tagval_t * tags = NULL; - void * newspace; - int ntags = 0; - int ntagspace = 0; - off_t offset = 0; - int ret; - size_t shift; - glite_jp_error_t err; - - glite_jp_clear_error(ctx); - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - - ntagspace = 1; - tags = (glite_jp_tagval_t *) calloc(ntagspace + 1, sizeof(*tags)); - if (!tags) { - err.code = ENOMEM; - return glite_jp_stack_error(ctx,&err); - } - while (!(ret = glite_jpps_tag_read(ctx, handle, offset, &tags[ntags], &shift))) { - offset += shift; - ntags++; - if (ntagspace <= ntags) { - ntagspace += 1; - newspace = realloc(tags, (ntagspace + 1) * sizeof(*tags)); - if (!newspace) { - err.code = ENOMEM; - goto error_out; - } - tags = (glite_jp_tagval_t *) newspace; - } - } - if (ret == ENOENT) { - *tags_out = tags; - return 0; - } else { - err.code = EIO; - err.desc = "Error reading tag value"; - } - -error_out: - for (; ntags-- ;) { - free(tags[ntags].name); - free(tags[ntags].value); - } - free(tags); - return glite_jp_stack_error(ctx,&err); -} diff --git a/org.glite.jp.primary/src/tags.h b/org.glite.jp.primary/src/tags.h deleted file mode 100644 index 3aade74..0000000 --- a/org.glite.jp.primary/src/tags.h +++ /dev/null @@ -1 +0,0 @@ -int glite_jpps_tag_append(glite_jp_context_t,void *,const char *, const char *); diff --git a/org.glite.jp.primary/src/tags_plugin.c b/org.glite.jp.primary/src/tags_plugin.c deleted file mode 100644 index 877201d..0000000 --- a/org.glite.jp.primary/src/tags_plugin.c +++ /dev/null @@ -1,149 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "file_plugin.h" -#include "builtin_plugins.h" -#include "backend.h" - -static int tagappend(void *,void *,int,...); -static int tagopen(void *,void *,const char *uri,void **); -static int tagclose(void *,void *); - -#define TAGS_MAGIC 0x74c016f2 /* two middle digits encode version, i.e. 01 */ - -static int tagdummy() -{ - puts("tagdummy()"); - return -1; -} - -struct tags_handle { - void *bhandle; - int n; - glite_jp_tagval_t *tags; -}; - -int init(glite_jp_context_t ctx, glite_jpps_fplug_data_t *data) -{ - data->fpctx = ctx; - - data->uris = calloc(2,sizeof *data->uris); - data->uris[0] = strdup(GLITE_JP_FILETYPE_TAGS); - - data->classes = calloc(2,sizeof *data->classes); - data->classes[0] = strdup("tags"); - - data->ops.open = tagopen; - data->ops.close = tagclose; - data->ops.attr = tagdummy; - data->ops.generic = tagappend; - - printf("tags_plugin: URI: \"%s\"; magic number: 0x%08lx\n",GLITE_JP_FILETYPE_TAGS,TAGS_MAGIC); - return 0; -} - -static int tagopen(void *fpctx,void *bhandle,const char *uri,void **handle) -{ - struct tags_handle *h = calloc(1,sizeof *h); - h->n = -1; - h->bhandle = bhandle; - - *handle = h; - - return 0; -} - -static int tagclose(void *fpctx,void *handle) -{ - int i; - struct tags_handle *h = handle; - - for (i=0; in; i++) { - free(h->tags[i].name); - free(h->tags[i].value); - } - free(h->tags); - free(h); - - return 0; -} - -static int tagappend(void *fpctx,void *handle,int oper,...) -{ - glite_jp_tagval_t *tag; - va_list ap; - char *hdr,*rec; - glite_jp_context_t ctx = fpctx; - struct tags_handle *h = handle; - uint32_t magic,hlen,rlen,rlen_n; - size_t r; - glite_jp_error_t err; - - memset(&err,0,sizeof err); - err.source = __FUNCTION__; - glite_jp_clear_error(ctx); - - va_start(ap,oper); - tag = va_arg(ap,glite_jp_tagval_t *); - va_end(ap); - - printf("tagappend: %s,%d,%s\n",tag->name,tag->sequence,tag->value); - - assert(oper == GLITE_JP_FPLUG_TAGS_APPEND); - - if (glite_jppsbe_pread(ctx,h->bhandle,&magic,sizeof magic,0,&r)) { - err.code = EIO; - err.desc = "reading magic number"; - return glite_jp_stack_error(ctx,&err); - } - - if (r == 0) { - magic = htonl(TAGS_MAGIC); - if (glite_jppsbe_pwrite(ctx,h->bhandle,&magic,sizeof magic,0)) { - err.code = EIO; - err.desc = "writing magic number"; - return glite_jp_stack_error(ctx,&err); - } - } - else if (r != sizeof magic) { - err.code = EIO; - err.desc = "can't read magic number"; - return glite_jp_stack_error(ctx,&err); - } - else if (magic != htonl(TAGS_MAGIC)) { - err.code = EINVAL; - err.desc = "invalid magic number"; - return glite_jp_stack_error(ctx,&err); - } - - trio_asprintf(&hdr,"%d %ld %c",tag->sequence, - tag->timestamp,tag->binary ? 'B' : 'S'); - - rlen = strlen(tag->name) + strlen(hdr) + 2 /* \0 after name and after hdr */ + - (r = tag->binary ? tag->size : (tag->value ? strlen(tag->value) : 0)); - - rlen_n = htonl(rlen); - - rec = malloc(rlen + sizeof rlen_n); - *((uint32_t *) rec) = rlen_n; - strcpy(rec + sizeof rlen_n,tag->name); - strcpy(rec + (hlen = sizeof rlen_n + strlen(tag->name) + 1),hdr); - - if (r) memcpy(rec + hlen + strlen(hdr) + 1,tag->value,r); - free(hdr); - - if (glite_jppsbe_append(ctx,h->bhandle,rec,rlen + sizeof rlen_n)) { - err.code = EIO; - err.desc = "writing tag record"; - free(rec); - return glite_jp_stack_error(ctx,&err); - } - - return 0; -} diff --git a/org.glite.jp.primary/src/typemap.dat b/org.glite.jp.primary/src/typemap.dat deleted file mode 100644 index 72f515f..0000000 --- a/org.glite.jp.primary/src/typemap.dat +++ /dev/null @@ -1,3 +0,0 @@ -jpsrv = http://glite.org/wsdl/services/jp -jptype = http://glite.org/wsdl/types/jp -jpelem = http://glite.org/wsdl/elements/jp diff --git a/org.glite.jp.ws-interface/.cvsignore b/org.glite.jp.ws-interface/.cvsignore deleted file mode 100755 index 1df717b..0000000 --- a/org.glite.jp.ws-interface/.cvsignore +++ /dev/null @@ -1,2 +0,0 @@ -.project -.cdtproject \ No newline at end of file diff --git a/org.glite.jp.ws-interface/LICENSE b/org.glite.jp.ws-interface/LICENSE deleted file mode 100755 index 01b973b..0000000 --- a/org.glite.jp.ws-interface/LICENSE +++ /dev/null @@ -1,69 +0,0 @@ -LICENSE file for EGEE Middleware -================================ - -Copyright (c) 2004 on behalf of the EU EGEE Project: -The European Organization for Nuclear Research (CERN), -Istituto Nazionale di Fisica Nucleare (INFN), Italy -Datamat Spa, Italy -Centre National de la Recherche Scientifique (CNRS), France -CS Systeme d'Information (CSSI), France -Royal Institute of Technology, Center for Parallel Computers (KTH-PDC), Sweden -Universiteit van Amsterdam (UvA), Netherlands -University of Helsinki (UH.HIP), Finlan -University of Bergen (UiB), Norway -Council for the Central Laboratory of the Research Councils (CCLRC), United Kingdom - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - -1. Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - -2. Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - -3. The end-user documentation included with the redistribution, if -any, must include the following acknowledgment: "This product includes -software developed by The EU EGEE Project (http://cern.ch/eu-egee/)." -Alternatively, this acknowledgment may appear in the software itself, if -and wherever such third-party acknowledgments normally appear. - -4. The names EGEE and the EU EGEE Project must not be -used to endorse or promote products derived from this software without -prior written permission. For written permission, please contact -. - -5. You are under no obligation whatsoever to provide anyone with any -bug fixes, patches, or upgrades to the features, functionality or -performance of the Software ("Enhancements") that you may develop over -time; however, if you choose to provide your Enhancements to The EU -EGEE Project, or if you choose to otherwise publish or distribute your -Enhancements, in source code form without contemporaneously requiring -end users of The EU EGEE Proejct to enter into a separate written license -agreement for such Enhancements, then you hereby grant The EU EGEE Project -a non-exclusive, royalty-free perpetual license to install, use, copy, -modify, prepare derivative works, incorporate into the EGEE Middleware -or any other computer software, distribute, and sublicense your -Enhancements or derivative works thereof, in binary and source code -form (if any), whether developed by The EU EGEE Project or third parties. - -THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED -WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL PROJECT OR ITS CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR -BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, -WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE -OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN -IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -This software consists of voluntary contributions made by many -individuals on behalf of the EU EGEE Prject. For more information on The -EU EGEE Project, please see http://cern.ch/eu-egee/. For more information on -EGEE Middleware, please see http://egee-jra1.web.cern.ch/egee-jra1/ - - diff --git a/org.glite.jp.ws-interface/Makefile b/org.glite.jp.ws-interface/Makefile deleted file mode 100644 index ee80ab4..0000000 --- a/org.glite.jp.ws-interface/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -# Default values -top_srcdir=. -builddir=build -top_builddir=${top_srcdir}/${builddir} -stagedir=. -distdir=. -globalprefix=glite -package=glite-jp-ws-interface -version=0.0.0 -PREFIX=/opt/glite - --include Makefile.inc - -VPATH=${top_srcdir}/interface:${top_srcdir}/src -STAGETO=interface - -XSLTPROC=xsltproc -XMLLINT:=xmllint -docbookxls:=http://docbook.sourceforge.net/release/xsl/current/html/docbook.xsl - -#WSDL=JobProvenancePS.wsdl JobProvenanceIS.wsdl JobProvenanceTypes.wsdl -WSDL=JobProvenancePS.wsdl JobProvenanceTypes.wsdl - -all compile: ${WSDL} - -check: - @echo No unit test required for interface-only module. - -stage: ${WSDL} - $(MAKE) install PREFIX=${stagedir} - -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=${top_srcdir}/tmpbuilddir - cd ${top_srcdir}/tmpbuilddir && tar -czf ${top_srcdir}/${distdir}/${package}-${version}_bin.tar.gz * - rm -rf ${top_srcdir}/tmpbuilddir - -install: - -mkdir -p ${PREFIX}/${STAGETO} - -mkdir -p ${PREFIX}/share/doc/${package}-${version} - install -m 644 ${top_srcdir}/LICENSE ${PREFIX}/share/doc/${package}-${version} -# cd ${top_srcdir}/interface && install -m 644 ${WSDL} ${PREFIX}/${STAGETO} - install -m 644 ${WSDL} ${PREFIX}/${STAGETO} - -clean: - rm -f *.h - -%.wsdl: %.xml - ${XSLTPROC} ../src/puke-wsdl.xsl $< >$@ - -JobProvenancePS.html: doc.xml JobProvenancePS.xml JobProvenanceTypes.xml puke-ug.xsl - ${XSLTPROC} --novalid ../src/puke-ug.xsl $< >doc-html.xml - ${XMLLINT} --valid --noout doc-html.xml - ${XSLTPROC} --stringparam chapter.autolabel 0 ${docbookxls} doc-html.xml >$@ diff --git a/org.glite.jp.ws-interface/build.xml b/org.glite.jp.ws-interface/build.xml deleted file mode 100644 index ae5d49b..0000000 --- a/org.glite.jp.ws-interface/build.xml +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.ws-interface/interface/JobProvenanceIS.wsdl b/org.glite.jp.ws-interface/interface/JobProvenanceIS.wsdl deleted file mode 100644 index 84c8844..0000000 --- a/org.glite.jp.ws-interface/interface/JobProvenanceIS.wsdl +++ /dev/null @@ -1,110 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - Store or update information on jobs within the JP index server. -Called directly by the primary storage, used for both batch and incremental feed. - -Input: - -data: list of job record updates. Each contains jobid, list of JP attribute values and user tag values. - -feedDone: flag indicating end of batch feed. (In order to avoid potential problems with buffer allocation -the huge dataset of batch feed is split into reasonable chunks and delivered with more UpdateJobs calls.) - -Output: N/A - -Faults: GenericJPFault - - - - - - - - - Retrieve pointers to job records of jobs matching a query. -Input: conditions - list of lists of query conditions. - Elements of the inner lists refer to a single job attribute, the conditions are or-ed. - Elements of the outer list may refer to different job attributes, they are and-ed. - -Output: - -jobs: list of JobId, PSContact (URL of the primary storage which manges this job) pairs - -Faults: GenericJPFault - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Job Provenance Index service - - - - - - - diff --git a/org.glite.jp.ws-interface/project/build.number b/org.glite.jp.ws-interface/project/build.number deleted file mode 100644 index 63f5995..0000000 --- a/org.glite.jp.ws-interface/project/build.number +++ /dev/null @@ -1 +0,0 @@ -module.build=33 diff --git a/org.glite.jp.ws-interface/project/build.properties b/org.glite.jp.ws-interface/project/build.properties deleted file mode 100755 index e69de29..0000000 diff --git a/org.glite.jp.ws-interface/project/configure.properties.xml b/org.glite.jp.ws-interface/project/configure.properties.xml deleted file mode 100644 index 4b08208..0000000 --- a/org.glite.jp.ws-interface/project/configure.properties.xml +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - - - -top_srcdir=.. -builddir=build -stagedir=${stage.abs.dir} -distdir=${dist.dir} -globalprefix=${global.prefix} -jpprefix=${subsystem.prefix} -package=${module.package.name} -PREFIX=${install.dir} -version=${module.version} -glite_location=${with.glite.location} - - - diff --git a/org.glite.jp.ws-interface/project/glite-jp-ws-interface.spec b/org.glite.jp.ws-interface/project/glite-jp-ws-interface.spec deleted file mode 100644 index dba6d55..0000000 --- a/org.glite.jp.ws-interface/project/glite-jp-ws-interface.spec +++ /dev/null @@ -1,42 +0,0 @@ -Summary:Change me !!! -Name:glite-jp-ws-interface -Version:0.0.0 -Release:0 -Copyright:Open Source EGEE License -Vendor:EU EGEE project -Group:System/Application -Prefix:/opt/glite -BuildArch:x86_64 -BuildRoot:%{_builddir}/%{name}-%{version} -Source:glite-jp-ws-interface-0.0.0_bin.tar.gz - -%define debug_package %{nil} - -%description -Change me !!! - -%prep - - -%setup -c - -%build - - -%install - - -%clean - -%pre -%post -%preun -%postun -%files -%defattr(-,root,root) -%{prefix}/interface/JobProvenancePS.wsdl -%{prefix}/interface/JobProvenanceTypes.wsdl -%{prefix}/share/doc/glite-jp-ws-interface-0.0.0/LICENSE - -%changelog - diff --git a/org.glite.jp.ws-interface/project/properties.xml b/org.glite.jp.ws-interface/project/properties.xml deleted file mode 100644 index 4ec8018..0000000 --- a/org.glite.jp.ws-interface/project/properties.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp.ws-interface/project/tar_exclude b/org.glite.jp.ws-interface/project/tar_exclude deleted file mode 100644 index e69de29..0000000 diff --git a/org.glite.jp.ws-interface/project/version.properties b/org.glite.jp.ws-interface/project/version.properties deleted file mode 100755 index 0ff5227..0000000 --- a/org.glite.jp.ws-interface/project/version.properties +++ /dev/null @@ -1,2 +0,0 @@ -module.version=1.0.0 -module.age=0 diff --git a/org.glite.jp.ws-interface/src/JobProvenancePS.xml b/org.glite.jp.ws-interface/src/JobProvenancePS.xml deleted file mode 100644 index c4cb810..0000000 --- a/org.glite.jp.ws-interface/src/JobProvenancePS.xml +++ /dev/null @@ -1,112 +0,0 @@ - - - CVS revision: - - - - - - The Job Provenance (JP) Primary Storage Service is responsible - to keep the JP data - (definition of submitted jobs, execution conditions and environment, - and important points of the job life cycle) in a compact and - economic form. - - - - The JP Primary storage, as described in section 8.4 of - the - - Architecture deliverable DJRA1.1 - - provides public interfaces for data storing, - retrieval based on basic metadata, and registration of Index servers for - incremental feed. - - - - Command interface to JP is completely covered by the WS interface covered here. - Bulk file transfers are done via specialised protocols, currently gsiftp only. - - - - - - - - Register job with the JP primary storage. - Jobid of the registered job. - Owner of the job (DN of X509 certificate). - Any error. - - - - Start uploading a file. - Jobid to which this file is related. - - Type of the file (URI). The server must have a plugin handing this type. - - Name of the file (used to distinguish among more files of the same type). - The client promisses to finish the upload before this time. - MIME type of the file. - URL where the client should upload the file. - Server's view on when the upload must be finished. - Any error. - - - - Confirm a successfully finished file apload. - Destination URL returned by StartUpload before. - Any error. - - - - Record an additional user tag. - Job to which the tag is added. - Name and value of the tag. - Any error. - - - - Request for feeding a JP Index server (issued by this server). - Endpoint of the listening index server. - Which attributes of jobs is the index server interested in. - Which jobs is the server interested in. - Data on jobs stored at PS in the past are required. - Data on jobs that will arrive in future are required. - Unique ID of the created feed session. - When the session expires. - Any error. - - - - Refresh an existing feed session. - Existing feed session ID to be refreshed. - New session expiration time. - Any error. - - - - Return URL's of files for a given single job. - The job. - List of the stored files. - Any error. - - - - Query concrete attributes of a given job. - The job. - Which attributes should be retrieved. - Values of the queried attributes. - Any error. - - - - diff --git a/org.glite.jp.ws-interface/src/JobProvenanceTypes.xml b/org.glite.jp.ws-interface/src/JobProvenanceTypes.xml deleted file mode 100644 index 49d3bf6..0000000 --- a/org.glite.jp.ws-interface/src/JobProvenanceTypes.xml +++ /dev/null @@ -1,75 +0,0 @@ - - - - - CVS revision: - - - - - Operators used in queries. Most are self-explanatory. - - - - - The attribute is between two specified values. - - - - A single user-recorded value for a job attribute. - Name of the attribute, including namespace. - Value - printable string. - - - - - - - - - - - - A single condition on job. - Attribute name to query. - Operation. - Where the attribute value came from. - Value to compare the job attribute with. - Another value (for op = WITHIN). - - - - JP primary storage file identification. - Type of the file (as set on StartUpload). - Name of the file (if there are more of the same type per job). - Where the file is stored on JP primary storage. - - - - Single value of an attribute. - Name of the attribute, including namespace. - String value. - When this value was recorded. - Where this value came from. - - - - - String value. - Binary value. - - - - Specification of attribute origin. - JP system value, e.g. job owner. - Explicitely stored by the user via RecordTag operation. - Coming from uploaded file. - - - - - - - diff --git a/org.glite.jp.ws-interface/src/doc.xml b/org.glite.jp.ws-interface/src/doc.xml deleted file mode 100644 index 78cb6a5..0000000 --- a/org.glite.jp.ws-interface/src/doc.xml +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/org.glite.jp.ws-interface/src/puke-ug.xsl b/org.glite.jp.ws-interface/src/puke-ug.xsl deleted file mode 100644 index ff70ccf..0000000 --- a/org.glite.jp.ws-interface/src/puke-ug.xsl +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - - - - - - <xsl:value-of select="document('JobProvenancePS.xml')/service/@name"/> - - - Overview - - - - - Operations - - - - - - - - - Types - - - - - - - - - - - - - list of - - - - - - - - - - - - - - - - - - - <xsl:value-of select="@name"/> - - - Inputs: - - - - - - - N/A - - - - Outputs: - - - - - - - N/A - - - - - - - - - - <xsl:value-of select="@name"/> - - - - Structure (sequence complex type in WSDL) - Fields: ( type name description ) - - - Union (choice complex type in WSDL) - Fields: ( type name description ) - - - Enumeration (restriction of xsd:string in WSDL), - exactly one of the values must be specified. - - Values: - - - Flags (sequence of restricted xsd:string in WSDL), - any number of values can be specified together. - - Values: - - - - - - - - - list of - - - - - - - - - - - - - - - - - - - - (optional) - - - - - - - - - - - - - diff --git a/org.glite.jp.ws-interface/src/puke-wsdl.xsl b/org.glite.jp.ws-interface/src/puke-wsdl.xsl deleted file mode 100644 index ea7598e..0000000 --- a/org.glite.jp.ws-interface/src/puke-wsdl.xsl +++ /dev/null @@ -1,292 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - true - false - - - - - - - - - - : - - - - - - 0 - 1 - - - - - unbounded - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - : - - - - - unbounded - 1 - - - - - - - - - - - - - - - : - - - - - unbounded - 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp/.cvsignore b/org.glite.jp/.cvsignore deleted file mode 100644 index 3a4edf6..0000000 --- a/org.glite.jp/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -.project diff --git a/org.glite.jp/build.xml b/org.glite.jp/build.xml deleted file mode 100644 index f51954b..0000000 --- a/org.glite.jp/build.xml +++ /dev/null @@ -1,268 +0,0 @@ - - - - - - - Ant build file to build the GLite Job Provenance Subsystem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Preparing directories ... - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <project name="${subsystem.name}" type="post-subsystem" packageName="${global.prefix}-${subsystem.prefix}"/> - - - - diff --git a/org.glite.jp/project/build.number b/org.glite.jp/project/build.number deleted file mode 100644 index c680c72..0000000 --- a/org.glite.jp/project/build.number +++ /dev/null @@ -1 +0,0 @@ -module.build=36 diff --git a/org.glite.jp/project/build.properties b/org.glite.jp/project/build.properties deleted file mode 100644 index e69de29..0000000 diff --git a/org.glite.jp/project/dependencies.properties b/org.glite.jp/project/dependencies.properties deleted file mode 100644 index ab3b83f..0000000 --- a/org.glite.jp/project/dependencies.properties +++ /dev/null @@ -1,12 +0,0 @@ -################################################################### -# System dependencies -################################################################### - -org.glite.version = HEAD -org.glite.jp.version = HEAD - -# Component dependencies tag = do not remove this line = -org.glite.jp.ws-interface.version = HEAD -org.glite.jp.common.version = HEAD -org.glite.jp.index.version = HEAD -org.glite.jp.primary.version = HEAD diff --git a/org.glite.jp/project/glite.jp.csf.xml b/org.glite.jp/project/glite.jp.csf.xml deleted file mode 100644 index fd68f71..0000000 --- a/org.glite.jp/project/glite.jp.csf.xml +++ /dev/null @@ -1,271 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - The org.glite and org.glite.jp modules have been updated, please rerun the configuration file - - - - - The org.glite and org.glite.jp modules have been updated, please rerun the configuration file - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp/project/properties.xml b/org.glite.jp/project/properties.xml deleted file mode 100755 index 276cf76..0000000 --- a/org.glite.jp/project/properties.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/org.glite.jp/project/run-workspace b/org.glite.jp/project/run-workspace deleted file mode 100644 index a5d1f54..0000000 --- a/org.glite.jp/project/run-workspace +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -cd ../.. - -cvs co org.glite -cvs co org.glite.jp - -cd org.glite.jp/project -ant -f glite.jp.csf.xml - diff --git a/org.glite.jp/project/taskdefs.xml b/org.glite.jp/project/taskdefs.xml deleted file mode 100755 index c4cc889..0000000 --- a/org.glite.jp/project/taskdefs.xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/org.glite.jp/project/version.properties b/org.glite.jp/project/version.properties deleted file mode 100644 index cd1e9e7..0000000 --- a/org.glite.jp/project/version.properties +++ /dev/null @@ -1,2 +0,0 @@ -module.version=1.0.0 -module.age=1 diff --git a/org.glite.lb.client/project/version.properties b/org.glite.lb.client/project/version.properties index 0ef0eb2..707ed52 100644 --- a/org.glite.lb.client/project/version.properties +++ b/org.glite.lb.client/project/version.properties @@ -1,3 +1,3 @@ #Thu May 19 13:15:52 CEST 2005 -module.version=2.0.0 +module.version=2.0.1 module.age=1 diff --git a/org.glite.lb.client/src/prod_proto.c b/org.glite.lb.client/src/prod_proto.c index 7d24851..448aac0 100644 --- a/org.glite.lb.client/src/prod_proto.c +++ b/org.glite.lb.client/src/prod_proto.c @@ -66,18 +66,55 @@ int edg_wll_log_proto_handle_gss_failures(edg_wll_Context context, int code, edg } -static edg_wll_Context tmp_context; -static edg_wll_PlainConnection *tmp_conn; +static int +read_il_data_thr(char **buffer, + int (*reader)(char *, const int,edg_wll_Context,void *),edg_wll_Context ctx,void *conn) +{ + char buf[17]; + int ret, len; + + /* read 17 byte header */ + len = (*reader)(buf, 17,ctx,conn); + if(len < 0) { + goto err; + } + buf[16] = 0; + if((len=atoi(buf)) <= 0) { + len = -1; + goto err; + } + + /* allocate room for the body */ + *buffer = malloc(len+1); + if(*buffer == NULL) { + len = -1; + goto err; + } + + /* read body */ + ret = (*reader)(*buffer, len,ctx,conn); + if(ret < 0) { + free(*buffer); + *buffer = NULL; + len = ret; + goto err; + } + + (*buffer)[len] = 0; + + err: + return(len); +} static int -plain_reader(char *buffer, int max_len) +plain_reader(char *buffer, int max_len,edg_wll_Context ctx,void *conn) { int len; - len = edg_wll_plain_read_full(tmp_conn, buffer, max_len, &tmp_context->p_tmp_timeout); + len = edg_wll_plain_read_full(conn, buffer, max_len, &ctx->p_tmp_timeout); if(len < 0) - edg_wll_SetError(tmp_context, LB_PROTO, "get_reply_plain(): error reading message data"); + edg_wll_SetError(ctx, LB_PROTO, "get_reply_plain(): error reading message data"); return(len); } @@ -98,9 +135,7 @@ get_reply_plain(edg_wll_Context context, edg_wll_PlainConnection *conn, char **b int len, code; code = 0; - tmp_context = context; - tmp_conn = conn; - len = read_il_data(&msg, plain_reader); + len = read_il_data_thr(&msg, plain_reader,context,conn); if(len < 0) goto get_reply_plain_end; @@ -116,20 +151,18 @@ get_reply_plain_end: } -static edg_wll_GssConnection *tmp_gss_conn; - static int -gss_reader(char *buffer, int max_len) +gss_reader(char *buffer, int max_len,edg_wll_Context ctx,void *conn) { int ret, len; edg_wll_GssStatus gss_code; - ret = edg_wll_gss_read_full(tmp_gss_conn, buffer, max_len, &tmp_context->p_tmp_timeout, + ret = edg_wll_gss_read_full(conn, buffer, max_len, &ctx->p_tmp_timeout, &len, &gss_code); if(ret < 0) { - edg_wll_log_proto_handle_gss_failures(tmp_context, ret, &gss_code, "edg_wll_gss_read_full"); - edg_wll_UpdateError(tmp_context, LB_PROTO, "get_reply_gss(): error reading message"); + edg_wll_log_proto_handle_gss_failures(ctx, ret, &gss_code, "edg_wll_gss_read_full"); + edg_wll_UpdateError(ctx, LB_PROTO, "get_reply_gss(): error reading message"); } return(ret); @@ -143,9 +176,7 @@ get_reply_gss(edg_wll_Context context, edg_wll_GssConnection *conn, char **buf, char *msg; int code; - tmp_context = context; - tmp_gss_conn = conn; - code = read_il_data(&msg, gss_reader); + code = read_il_data_thr(&msg, gss_reader,context, conn); if(code < 0) goto get_reply_gss_end; diff --git a/org.glite.lb.client/src/producer.c b/org.glite.lb.client/src/producer.c index 27c71fd..bd6905c 100644 --- a/org.glite.lb.client/src/producer.c +++ b/org.glite.lb.client/src/producer.c @@ -127,7 +127,7 @@ static int edg_wll_DoLogEvent( if ((answer = edg_wll_gss_connect(cred, context->p_destination, context->p_dest_port, &context->p_tmp_timeout, &con, &gss_stat)) < 0) { - edg_wll_log_proto_handle_gss_failures(context,answer,&gss_stat,"edg_wll_gss_connect()"); + answer = edg_wll_log_proto_handle_gss_failures(context,answer,&gss_stat,"edg_wll_gss_connect()"); goto edg_wll_DoLogEvent_end; } @@ -274,7 +274,7 @@ static int edg_wll_DoLogEventDirect( #endif if ((answer = edg_wll_gss_connect(cred,host,port, &context->p_tmp_timeout, &con, &gss_stat)) < 0) { - edg_wll_log_proto_handle_gss_failures(context,answer,&gss_stat,"edg_wll_gss_connect()"); + answer = edg_wll_log_proto_handle_gss_failures(context,answer,&gss_stat,"edg_wll_gss_connect()"); goto edg_wll_DoLogEventDirect_end; } diff --git a/org.glite.lb.common/project/version.properties b/org.glite.lb.common/project/version.properties index ad6ec55..c25d98c 100644 --- a/org.glite.lb.common/project/version.properties +++ b/org.glite.lb.common/project/version.properties @@ -1,3 +1,3 @@ #Thu May 19 13:15:32 CEST 2005 -module.version=2.0.0 +module.version=2.0.1 module.age=1 diff --git a/org.glite.lb.common/src/il_msg.c b/org.glite.lb.common/src/il_msg.c index a9e3fff..8c6e973 100644 --- a/org.glite.lb.common/src/il_msg.c +++ b/org.glite.lb.common/src/il_msg.c @@ -7,15 +7,18 @@ #include #include +#define IL_PROTOCOL_MAGIC_WORD "michal" + int encode_il_msg(char **buffer, const char *event) { int len; char *p; + char *protocol_magic_word = IL_PROTOCOL_MAGIC_WORD; /* allocate enough room to hold the message */ - len = 17 + len_string((char*)event); + len = 17 + len_string(protocol_magic_word) + len_string((char*)event); if((*buffer = malloc(len)) == NULL) { return(-1); } @@ -27,6 +30,7 @@ encode_il_msg(char **buffer, const char *event) p += 17; /* write rest of the message */ + p = put_string(p, protocol_magic_word); p = put_string(p, (char*)event); return(p - *buffer); @@ -59,8 +63,21 @@ int decode_il_msg(char **event, const char *buf) { char *p; + char *protocol_magic_word=NULL; + int magic_word_check_failed = 0; + + /* First check that the protocol 'magic' word is there */ + p = get_string((char*)buf, &protocol_magic_word); + if (protocol_magic_word) { + if (strcmp (protocol_magic_word, IL_PROTOCOL_MAGIC_WORD) != 0) { + magic_word_check_failed = 1; + } + free(protocol_magic_word); + } + + if (magic_word_check_failed != 0) return (-1); - p = get_string((char*)buf, event); + p = get_string(p, event); if(p == NULL) { if(*event) { free(*event); *event = NULL; }; return(-1); diff --git a/org.glite.lb.common/test/il_msg_test.cpp b/org.glite.lb.common/test/il_msg_test.cpp index 6748bdb..230b6f1 100644 --- a/org.glite.lb.common/test/il_msg_test.cpp +++ b/org.glite.lb.common/test/il_msg_test.cpp @@ -28,7 +28,7 @@ public: } void testEncodeMsg() { - CPPUNIT_ASSERT_EQUAL(len_msg, 26); + CPPUNIT_ASSERT_EQUAL(len_msg, 35); CPPUNIT_ASSERT(buffer_msg != NULL); CPPUNIT_ASSERT(!strncmp(buffer_msg, msg, len_msg)); } @@ -68,9 +68,9 @@ public: char *s; l = read_il_data(&s, test_reader); - CPPUNIT_ASSERT_EQUAL(l, 9); + CPPUNIT_ASSERT_EQUAL(l, 18); CPPUNIT_ASSERT(s != NULL); - CPPUNIT_ASSERT(!strcmp(s, "6 zprava\n")); + CPPUNIT_ASSERT(!strcmp(s, "6 michal\n6 zprava\n")); free(s); } @@ -87,7 +87,7 @@ private: } }; -const char *IlMsgTest::msg = " 9\n6 zprava\n"; +const char *IlMsgTest::msg = " 18\n6 michal\n6 zprava\n"; const char *IlMsgTest::rep = " 14\n10\n20\n5 chyba\n"; int IlMsgTest::pos; diff --git a/org.glite.lb.logger/project/version.properties b/org.glite.lb.logger/project/version.properties index b993122..f2e2528 100644 --- a/org.glite.lb.logger/project/version.properties +++ b/org.glite.lb.logger/project/version.properties @@ -1,3 +1,3 @@ #Thu May 19 13:16:38 CEST 2005 -module.version=1.1.1 +module.version=1.1.2 module.age=1 diff --git a/org.glite.lb.server/Makefile b/org.glite.lb.server/Makefile index a1ce060..adbcd6b 100644 --- a/org.glite.lb.server/Makefile +++ b/org.glite.lb.server/Makefile @@ -36,7 +36,11 @@ GSOAP_FILES_PREFIX:= bk_ws_ YACC=bison -y CC=gcc -VPATH=${top_srcdir}/src:${top_srcdir}/test:${top_srcdir}/examples:${top_srcdir}/project +ifeq ($(gsoap_version),2.7.0) + VPATH=${top_srcdir}/src:${top_srcdir}/test:${top_srcdir}/examples:${top_srcdir}/project:${gsoap_prefix} +else + VPATH=${top_srcdir}/src:${top_srcdir}/test:${top_srcdir}/examples:${top_srcdir}/project +endif AT3=perl -I${top_srcdir}/project ${top_srcdir}/project/at3 TEST_LIBS:=-L${cppunit}/lib -lcppunit @@ -56,7 +60,7 @@ CFLAGS:= \ -I${top_srcdir}/interface \ -I${expat_prefix}/include \ -I${ares_prefix}/include \ - -I${gsoap_prefix}/include \ + -I${gsoap_prefix}/include -I${gsoap_prefix}/ \ ${COVERAGE_FLAGS} \ -I${mysql_prefix}/include -I${mysql_prefix}/include/mysql \ -I${globus_prefix}/include/${nothrflavour} \ @@ -77,6 +81,12 @@ GLOBUS_LIBS:= -L${globus_prefix}/lib \ -lglobus_common_${nothrflavour} \ -lglobus_gssapi_gsi_${nothrflavour} \ +ifeq ($(shell ls ${gsoap_prefix}/bin/soapcpp2),${gsoap_prefix}/bin/soapcpp2) + gsoap_bin_prefix := ${gsoap_prefix}/bin +else + gsoap_bin_prefix := ${gsoap_prefix} +endif + ifneq (${mysql_prefix},/usr) ifeq ($(shell echo ${mysql_version} | cut -d. -f1,2),4.1) mysqlib := -L${mysql_prefix}/lib/mysql @@ -115,20 +125,33 @@ BKSERVER_BASE_OBJS:= \ lb_xml_parse_V21.o \ lock.o openserver.o query.o userjobs.o db_store.o request.o store.o \ stored_master.o srv_purge.o server_state.o dump.o lb_authz.o load.o \ - notification.o il_notification.o notif_match.o stats.o + notification.o il_notification.o notif_match.o stats.o ifeq ($(GLITE_LB_SERVER_WITH_WS),yes) - BKSERVER_OBJS:= \ - ${BKSERVER_BASE_OBJS} \ - ${GSOAP_FILES_PREFIX}C.o ${GSOAP_FILES_PREFIX}Server.o \ - ws_query.o ws_fault.o ws_typeref.o - - BKSERVER_LIBS= \ - ${SRVBONES_LIB} \ - -lglite_lb_common_${nothrflavour} \ - -L${gsoap_prefix}/lib -lgsoap \ - -lglite_security_gsoap_plugin_${nothrflavour} \ - ${EXT_LIBS} + ifeq ($(gsoap_version),2.7.0) + BKSERVER_OBJS:= \ + ${BKSERVER_BASE_OBJS} \ + ${GSOAP_FILES_PREFIX}C.o ${GSOAP_FILES_PREFIX}Server.o \ + ws_query.o ws_fault.o ws_typeref.o stdsoap2.o + + BKSERVER_LIBS= \ + ${SRVBONES_LIB} \ + -lglite_lb_common_${nothrflavour} \ + -lglite_security_gsoap_plugin_${nothrflavour} \ + ${EXT_LIBS} + else + BKSERVER_OBJS:= \ + ${BKSERVER_BASE_OBJS} \ + ${GSOAP_FILES_PREFIX}C.o ${GSOAP_FILES_PREFIX}Server.o \ + ws_query.o ws_fault.o ws_typeref.o + + BKSERVER_LIBS= \ + ${SRVBONES_LIB} \ + -lglite_lb_common_${nothrflavour} \ + -L${gsoap_prefix}/lib -lgsoap \ + -lglite_security_gsoap_plugin_${nothrflavour} \ + ${EXT_LIBS} + endif else BKSERVER_OBJS:= ${BKSERVER_BASE_OBJS} @@ -191,7 +214,7 @@ lb_xml_parse.c: lb_xml_parse.c.T chmod -w $@ >/dev/null ${GSOAP_FILES_PREFIX}H.h ${GSOAP_FILES_PREFIX}C.c ${GSOAP_FILES_PREFIX}Server.c ${GSOAP_FILES_PREFIX}Client.c ${GSOAP_FILES_PREFIX}ServerLib.c ${GSOAP_FILES_PREFIX}ClientLib.c LoggingAndBookkeeping.nsmap: LB.xh - ${gsoap_prefix}/bin/soapcpp2 -w -c -p ${GSOAP_FILES_PREFIX} LB.xh + ${gsoap_bin_prefix}/soapcpp2 -w -c -p ${GSOAP_FILES_PREFIX} LB.xh # try several times -- LB.wsdl downloads BaseFault.xsd from www.ibm.com which may be failing # not used right now but may be useful one day @@ -203,7 +226,7 @@ ${GSOAP_FILES_PREFIX}H.h ${GSOAP_FILES_PREFIX}C.c ${GSOAP_FILES_PREFIX}Server.c LB.xh: ws_typemap.dat ${stagedir}/interface/LB.wsdl cp ${stagedir}/interface/LBTypes.wsdl . - ${gsoap_prefix}/bin/wsdl2h -c -t ${top_srcdir}/src/ws_typemap.dat -o $@ ${stagedir}/interface/LB.wsdl + ${gsoap_bin_prefix}/wsdl2h -c -t ${top_srcdir}/src/ws_typemap.dat -o $@ ${stagedir}/interface/LB.wsdl rm -f LBTypes.wsdl test.xml: test_xml @@ -324,7 +347,7 @@ test_query_events.o: %.o: %.cpp ${COMPILE} -o $@ -c $< soap_version.h: - ${gsoap_prefix}/bin/soapcpp2 /dev/null + ${gsoap_bin_prefix}/soapcpp2 /dev/null perl -ne '$$. == 2 && /.*([0-9])\.([0-9])\.([0-9]).*/ && printf "#define GSOAP_VERSION %d%02d%02d\n",$$1,$$2,$$3' soapH.h >$@ -rm soapC.cpp soapH.h soapStub.h soapClient.cpp soapServer.cpp soapClientLib.cpp soapServerLib.cpp diff --git a/org.glite.lb.server/project/configure.properties.xml b/org.glite.lb.server/project/configure.properties.xml index ffe0fff..5e1b1da 100644 --- a/org.glite.lb.server/project/configure.properties.xml +++ b/org.glite.lb.server/project/configure.properties.xml @@ -20,6 +20,9 @@ Revision history: $Log$ + Revision 1.7 2005/08/03 09:30:28 akrenek + Merged the release 1.0 branch + Revision 1.6 2005/01/21 11:27:44 jpospi completely remove gridsite.prefix and voms.prefix @@ -85,6 +88,7 @@ mysql_prefix=${with.mysql.prefix} mysql_version=${ext.mysql.version} cppunit=${with.cppunit.prefix} gsoap_prefix=${with.gsoap.prefix} +gsoap_version=${ext.gsoap.version} diff --git a/org.glite.lb.server/project/version.properties b/org.glite.lb.server/project/version.properties index 21bc091..142f24a 100644 --- a/org.glite.lb.server/project/version.properties +++ b/org.glite.lb.server/project/version.properties @@ -1,3 +1,3 @@ #Thu May 19 13:16:56 CEST 2005 -module.version=1.2.4 +module.version=1.2.5 module.age=1 diff --git a/org.glite.lb/project/dependencies.properties b/org.glite.lb/project/dependencies.properties index 6d9ae2f..50e2ffb 100644 --- a/org.glite.lb/project/dependencies.properties +++ b/org.glite.lb/project/dependencies.properties @@ -3,15 +3,25 @@ # System dependencies ################################################################### -org.glite.version = HEAD -org.glite.lb.version = HEAD + + + org.glite.version = glite_R_1_0_5 + org.glite.lb.version = glite-lb_R_1_1_1 + + org.glite.lb.client-interface.version = glite-lb-client-interface_R_2_0_0 + + org.glite.lb.ws-interface.version = glite-lb-ws-interface_R_2_0_0 + + org.glite.lb.common.version = glite-lb-common_R_2_0_1 + + org.glite.lb.client.version = glite-lb-client_R_2_0_1 + + org.glite.lb.server-bones.version = glite-lb-server-bones_R_2_0_0 + + org.glite.lb.logger.version = glite-lb-logger_R_1_1_2 + + org.glite.lb.server.version = glite-lb-server_R_1_2_5 + + org.glite.lb.proxy.version = glite-lb-proxy_R_1_1_1 + -# Component dependencies tag = do not remove this line = -org.glite.lb.client-interface.version = HEAD -org.glite.lb.ws-interface.version = HEAD -org.glite.lb.common.version = HEAD -org.glite.lb.client.version = HEAD -org.glite.lb.server.version = HEAD -org.glite.lb.proxy.version = HEAD -org.glite.lb.server-bones.version = HEAD -org.glite.lb.logger.version = HEAD diff --git a/org.glite.lb/project/version.properties b/org.glite.lb/project/version.properties index 0f20f80..0670fcd 100644 --- a/org.glite.lb/project/version.properties +++ b/org.glite.lb/project/version.properties @@ -1,3 +1,3 @@ #Thu May 19 13:17:34 CEST 2005 -module.version=1.1.0 +module.version=1.1.1 module.age=1 diff --git a/org.glite.security.gsoap-plugin/Makefile b/org.glite.security.gsoap-plugin/Makefile index 6fce5b1..4e33f8c 100644 --- a/org.glite.security.gsoap-plugin/Makefile +++ b/org.glite.security.gsoap-plugin/Makefile @@ -14,6 +14,7 @@ globus_prefix=/opt/globus nothrflavour=gcc32 thrflavour=gcc32pthr gsoap_prefix=/opt/gsoap +gsplugin_version_checking=yes CC=gcc @@ -21,7 +22,6 @@ CC=gcc -include ../Makefile.inc GSPLUGIN_DEBUG?=no -GSPLUGIN_VERSION_CHECKING?=yes version_info=-version-info `echo ${version} | cut -d. -f1,2 | tr . :` @@ -30,7 +30,7 @@ VPATH=${top_srcdir}/src:${top_srcdir}/test:${top_srcdir}/examples TEST_LIBS:=-L${cppunit}/lib -lcppunit TEST_INC:=-I${cppunit}/include -ifeq ($(GSPLUGIN_DEBUG),yes) +ifeq ($(gsplugin_version_checking),yes) DEBUG:=-g -O0 -Wall -DGSPLUGIN_DEBUG else DEBUG:=-g -O0 -Wall @@ -67,6 +67,8 @@ GSOAP_LIBS:= -L${gsoap_prefix}/lib -lgsoap EX_LIBS:= ${GLOBUS_LIBS} -L${ares_prefix}/lib -lares +PATCHED_GSOAP_SRC:=stdsoap2_2.6.2.c + HDRS:=glite_gss.h glite_gsplugin.h GSS_OBJS:=glite_gss.o @@ -199,6 +201,7 @@ install: cd ${top_srcdir}/interface && ${INSTALL} -m 644 ${HDRS} ${PREFIX}/include/glite/security/ if [ x${DOSTAGE} = xyes ]; then \ install -m 644 ${GSS_STATICLIB} ${GSS_THRSTATICLIB} ${STATICLIB} ${THRSTATICLIB} ${PREFIX}/lib; \ + install -m 644 ${top_srcdir}/src/${PATCHED_GSOAP_SRC} ${PREFIX}/lib; \ fi clean: diff --git a/org.glite.security.gsoap-plugin/build.xml b/org.glite.security.gsoap-plugin/build.xml index b93e59c..1a2fc90 100755 --- a/org.glite.security.gsoap-plugin/build.xml +++ b/org.glite.security.gsoap-plugin/build.xml @@ -76,7 +76,7 @@ Load version file ========================================= --> - + - + + - + + - + + - -

httpd-fileserver.conf

- -

-httpd-fileserver.conf is an example -configuration file to use Apache/GridSite as a read/write HTTP(S) -fileserver, including comments on how to get the server up and running. - -

httpd-webserver.conf

- -

-httpd-webserver.conf is an example -configuration file to use Apache/GridSite as a Web Server -(that is, primarily for interactive use with a browser) -including comments on how to get the server up and running. - -

GridSite Directives

- -

-The mod_gridsite reference lists all the GridSite -httpd.conf directives. - -

-To start serving files, make a directory /var/www/htdocs owned by -nobody.nobody, including the .gacl access control file described below, -and add the following directive to the HTTPS <Directory> section: - -

-GridSiteMethods GET PUT DELETE - -

-If you wish to accept Globus GSI Proxies as well as full X.509 user -certificates, set GridSiteGSIProxyLimit to the depth of proxy you -wish to accept. (As a _rough_ guide: 0=No Proxies; 1=Proxy on user's -machine; 2=Proxy owned by running Globus job; 3=Proxy delegated by a -Globus job.) - -

GACL access control

- -

-The GACL reference explains the XML access -control files used by GridSite. These allow flexible policies to be written, -in terms of X.509 user certificates, GSI proxies, VOMS attribute -certificates, DN List groups and DNS hostnames. - -

-For example, to give all clients read and list permission: -

-

-<gacl>
-<entry>
-  <any-user/>
-  <allow><read/><list/></allow>
-</entry>
-</gacl>
-
- -

-To enable writing, add DN List, Person or VOMS entries to the file. -For example: - -

-

-<gacl>
-<entry>
-  <any-user/>
-  <allow><read/><list/></allow>
-</entry>
-<entry>
-  <person>
-  <dn>/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab</dn>
-  </person>
-  <allow><write/></allow>
-</entry>
-</gacl>
-
- -

-The GACL file that governs a directory is stored as .gacl in that directory. -If no .gacl is present, then GridSite will search the parent directories in -ascending order until one is found. - - - - diff --git a/org.gridsite.core/doc/delegation-1.wsdl b/org.gridsite.core/doc/delegation-1.wsdl new file mode 100644 index 0000000..35c46ef --- /dev/null +++ b/org.gridsite.core/doc/delegation-1.wsdl @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Starts the delegation procedure by asking for a certificate + signing request from the server. The server answers with a + certificate signing request which includes the public key + for the new delegated credentials. Uses PEM encoding. + + + + + + + + + Finishes the delegation procedure by sending the signed + proxy certificate to the server. Uses PEM encoding. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/org.gridsite.core/doc/findproxyfile.1 b/org.gridsite.core/doc/findproxyfile.1 index ae2f944..9de4d47 100644 --- a/org.gridsite.core/doc/findproxyfile.1 +++ b/org.gridsite.core/doc/findproxyfile.1 @@ -1,4 +1,4 @@ -.TH findproxyfile 1 "October 2004" findproxyfile "FINDPROXYFILE Manual" +.TH findproxyfile 1 "October 2004" "findproxyfile" "GridSite Manual" .SH NAME .B findproxyfile \- returns full path to GSI Proxy file diff --git a/org.gridsite.core/doc/gacl.html b/org.gridsite.core/doc/gacl.html deleted file mode 100644 index 82be605..0000000 --- a/org.gridsite.core/doc/gacl.html +++ /dev/null @@ -1,84 +0,0 @@ -GridSite: Grid Access Control Language - -

GridSite: Grid Access Control Language

- -

-GACL is the authorization policy language used by -GridSite GACL allows -policies to be written in terms of common Grid credentials: X.509 -identities, GSI proxies, VOMS attribute certificates and lists of X.509 -identities. - -

-GridSite both uses GACL policies and provides a GACL manipulation API for -C/C++ in the GridSite library. - -

Credentials

- -

-In GridSite 1.1.x, four credential types are supported: - -

-<person> -<dn>/O=Grid/CN=Name</dn> -</person> - -

-<voms> -<fqan>/vo.dom.ain/group</fqan> -</voms> - -

-<dn-list> -<url>https://www.vo.dom.ain/dn-lists/group</url> -</dn-list> - -

-<dns> -<hostname>host*.dom.ain</hostname> -</dns> - -

Permissions

- -

-Five permissions are supported: Admin, Write, List, Exec and Read. Admin is -permission to modify the authorization policy itself, but applications can -map the other permissions to local methods as appropriate to their -environment. For filesystems and fileservers, Write, List and Read have -their usual meanings: creating or modifying files or directories; browsing -directories; reading files. Exec is not used by GridSite itself, and -applications are free to give it a meaning within their own contexts. - -

-In 1.0.x, only per-directory GACL files are supported, and the file is stored -in the directory in question, or in one of its parent directories. (GridSite -searches upwards until it finds one.) - -

-In GACL files, the permissions are represented by single tags: -<admin/>, <write/>, <list/>, <exec/>, <read/>. -Permission -tags are contained within Allow or Deny blocks. For example: -<allow><read/><list/></allow> or -<deny><admin/></deny>. - -

Entries

- -

-Entries associate credentials with permission statements. Entries consist of -one or more credential blocks, and either an Allow or a Deny block, or both. -If multiple credentials are present in one entry, they must all be held by a -user to receive the association permissions. (So Entries provide logical AND -of credentials.) - -

Access Control Lists

- -

-ACLs consist of a list of one or more Entry blocks. When a user's credentials -are compared to the ACL, the permissions given to the user by Allow blocks -are recorded, along with those forbidden by Deny blocks. When all entries -have been evaluated, any forbidden permissions are removed from those -granted. (So Deny always wins over Allow, even between different Entries, -but otherwise ACLs provide logical OR of credentials.) - - diff --git a/org.gridsite.core/doc/gsexec.8 b/org.gridsite.core/doc/gsexec.8 index fbc5a62..e229663 100644 --- a/org.gridsite.core/doc/gsexec.8 +++ b/org.gridsite.core/doc/gsexec.8 @@ -1,56 +1,134 @@ -.de Sh \" Subsection -.br -.if t .Sp -.ne 5 -.PP -\fB\\$1\fR -.PP -.. -.de Sp \" Vertical space (when we can't use .PP) -.if t .sp .5v -.if n .sp -.. -.de Ip \" List item -.br -.ie \\n(.$>=3 .ne \\$3 -.el .ne 3 -.IP "\\$1" \\$2 -.. -.TH "GSEXEC" 8 "2005-05-27" "GridSite Apache Extensions" "gsexec" - +.TH GSEXEC 8 "October 2005" "gsexec" "GridSite Manual" .SH NAME -gsexec \- Switch user before executing external programs +.B gsexec +\- Switch user before executing external programs .SH "SYNOPSIS" -.PP -\fBgsexec\fR -\fBV\fR +.BR gsexec +[-V] - .SH "SUMMARY" - -.PP + gsexec is used by the Apache HTTP Server to switch to another user before executing CGI programs\&. In order to achieve this, it must run as root\&. Since the HTTP daemon normally doesn't run as root, the gsexec executable needs the setuid bit set and must be owned by root\&. It should never be writable for any other person than root\&. -.PP -gsexec is based on Apache's suexec. -For further information about the concepts and the security model of -the original suexec -please refer to the suexec documentation: +gsexec is based on Apache's suexec, and its behaviour is controlled with +the Apache configuration file directives +.BR GridSiteExecMethod +and +.BR GridSiteUserGroup +added to Apache by +.BR mod_gridsite(8) +Four execution methods are supported: nosetuid, suexec, X509DN and directory, +and these may be set on a per-directory basis within the Apache configuration +file. + +.SH "NOSETUID METHOD" + +This is the default behaviour, but can also be produced by giving +.BR "GridSiteExecMethod nosetuid" + +CGI programs will then be executed without using gsexec, and will +run as the Unix user given by the User and Group Apache directives (normally +apache.apache on Red Hat derived systems.) + +.SH "SUEXEC METHOD" + +If +.BR "GridSiteExecMethod suexec" +is given for this virtual host or directory, then CGI programs will be +executed using the user and group given by the +.BR "GridSiteUserGroup user group" +directive, which may also be set on a per-directory basis (unlike suexec's +.BR SuexecUserGroup +which is per-server only.) The CGI program must either be owned by root, +the Apache user +and group specified at gsexec build-time (normally apache.apache) or by +the user and group given with the +.BR GridSiteUserGroup +directive. + +.SH "X509DN METHOD" + +If +.BR "GridSiteExecMethod X509DN" +is given, then the CGI program runs as a pool user, detemined using lock +files in the exec mapping directory chosen as build time of gsexec. +The pool user is chosen according +to the client's full certificate X.509 DN (ie with any trailing GSI proxy +name components stripped off.) Subsequent requests by the same X.509 +identity will be mapped to the same pool user. The CGI program must either be +owned by root, the Apache user +and group specified at gsexec build-time (normally apache.apache) or by +the pool user selected. + +.SH "DIRECTORY METHOD" + +If +.BR "GridSiteExecMethod directory" +is given, then the CGI program runs as a pool user chosen according +to the directory in which the CGI is located: all CGIs in that directory +run as the same pool user. The CGI program must either be +owned by root, the Apache user +and group specified at gsexec build-time (normally apache.apache) or by +the pool user selected. -(http://httpd\&.apache\&.org/docs-2\&.0/suexec\&.html)\&. - + +.SH "EXECMAPDIR" + +The default exec mapping directory is /var/www/execmapdir and this is fixed +when the gsexec executable is built. The exec mapping directory and all +of its lock files must be owned and only writable by root. To initialise the +lock files, create an empty lock file for each pool user, with the pool +username as the filename (eg user0001, user0002, ...) As the pool users are +leased to X.509 identities or directories, they will become hard linked to +lock files with the URL-encoded X.509 DN or full directory path. + +You can recycle pool users by removing the corresponding URL-encoded +hard link. +.BR stat(1) +and +.BR "ls(1)" +with option +.BR "-i" +can be used to print the inodes of lock files to match up the hard links. + +.BR "However, you must ensure that all files and processes owned by the pool" +.BR "user are deleted before recycling!" .SH "OPTIONS" - .TP -V If you are root, this option displays the compile options of gsexec\&. For security reasons all configuration options are changeable only at compile time\&. +.SH "MORE INFORMATION" +For further information about the concepts and the security model of +the original Apache suexec +please refer to the suexec documentation: + +http://httpd\&.apache\&.org/docs-2\&.0/suexec\&.html + +For examples using the gsexec extensions, please see the GridSite gsexec +page: + +http://www.gridsite.org/wiki/Gsexec + +.SH AUTHORS + +Apache project, for original suexec + +Andrew McNab for gsexec modifications. + +gsexec is part of GridSite: http://www.gridsite.org/ + +.SH "SEE ALSO" +.BR httpd(8), +.BR suexec(8), +.BR mod_gridsite(8) diff --git a/org.gridsite.core/doc/htcp.1 b/org.gridsite.core/doc/htcp.1 index 984aaaf..39e20f5 100644 --- a/org.gridsite.core/doc/htcp.1 +++ b/org.gridsite.core/doc/htcp.1 @@ -1,18 +1,25 @@ -.TH htcp 1 "July 2004" htcp "HTCP Manual" +.TH HTCP 1 "October 2005" "htcp" "GridSite Manual" .SH NAME -.B htcp, htrm, htls, htll, htmkdir -\- get, put, delete or list HTTP/HTTPS files or directories +.B htcp, htmv, htrm, htls, htll, htmkdir, htfind, htping +\- file transfers and queries via HTTP/HTTPS/SiteCast .SH SYNOPSIS -.B htcp [options] -.I Source-URL[s] [Destination URL] +.B htcp, htmv +[options] Source-URL[s] Destination-URL + +.B htrm, htls, htll, htmkir, htfind +[options] Target-URL[s] + +.B htping +[options] .SH DESCRIPTION .B htcp is a client to fetch files or directory listings from remote servers using HTTP or HTTPS, or to put or delete files or directories onto remote servers using HTTPS. htcp is similar to scp(1), but uses HTTP/HTTPS rather than ssh -as its transfer protocol. +as its transfer protocol. htcp can also use the HTCP protocol to query +HTTP(S) fileservers via SiteCast. -When talking to an HTTPS server, htcp can run "anonymously", with a +When talking to a fileserver with HTTPS, htcp can run "anonymously", with a standard X.509 user certificate and key, or with a GSI Proxy. This makes htcp very useful in Grid environments where many users have certificates and where jobs and users have access to GSI proxies. @@ -22,9 +29,9 @@ htcp supports the file:, http: and https: URL schemes as sources and destinations. If no scheme is given, the URL scheme is assumed to be file: and relative to the current directory if not an absolute path. -If multiple sources are given, they will be used in turn and the destination -must be a directory (directories are indicated by a trailing /) However, -source and destination cannot both refer to remote servers. +If multiple sources are given during a copy, they will be used in turn and +the destination must be a directory (directories are indicated by a trailing +/) However, source and destination cannot both refer to remote servers. .SH OPTIONS .IP "-v/--verbose" @@ -37,7 +44,6 @@ Instead of copying files, delete all the URLs given on the command line. Calling the program as htrm has the same effect. .IP "--list" -.br Instead of copying files, output lists of files located in the URL-directories given on the command line. Calling the program as htls has the same effect. @@ -53,8 +59,41 @@ with HTTP PUT. The server must support the convention that PUT to a URL with a trailing slash means create a directory. No file body is sent. Calling the program as htmkdir has the same effect. +.IP "--move" +Move/rename files on a single remote server, given the two, absolute URLs +of the remote file names. Server must support HTTP/WebDAV MOVE. Calling the +program as htmv has the same effect. + +.IP "--ping" +Query specified multicast groups with the HTCP NOP ("No Operation") code. +SiteCast enabled servers will respond immediately with a NOP reply, and all +of the responses will be listed, with the round trip time in milliseconds. +Any waiting times specified in the --groups option will be ignored. Calling +the program as htping has the same effect. +(--groups must be used for this option to work.) + +.IP "--find" +Query specified multicast groups with the HTCP TST code. SiteCast enabled +servers will respond with TST replies if they have the files corresponding +to the given SiteCast target URL(s). All of the transfer URLs returned +will be listed. Waiting times specified in the --groups option will be used +to space out the multicast queries, but the program listens for responses +continuously. Calling the program as htfind has the same effect. +(--groups must be used for this option to work.) + +.IP "--groups " +IP multicast groups to use for SiteCast queries. IP Groups is a comma +separated list of groups, in the format: nnn.nnn.nnn.nnn:port[:ttl[:seconds]] +The IP number and port must be specified. The IP time-to-live, ttl, controls +how many networks the multicast packets may pass through - the default, 1, +limits packets to the local network. Multiple groups may be specified, +separated by commas. If multiple groups are specified, then seconds is the +time to wait before making the next multicast - 1 second is the default. + +.IP "--timeout " +A request timeout used for multicast ping. + .IP "--anon" -.br Do not attempt to use X.509 user certificates or GSI proxies to authenticate to the remote HTTPS server. This means you are "anonymous", but the server's identity may still be verified and the connection is still encrypted. @@ -84,14 +123,30 @@ This is useful for testing sites before their certificate is set up properly, but leaves you vulnerable to "man in the middle" attacks by hostile servers masquerading as your target. -.IP "--downgrade-size " -Try to use HTTP-Downgrade for HTTPS URLs. Compatible servers will perform +.IP "--grid-http" +Try to use GridHTTP redirection for HTTPS URLs. Compatible servers will perform authentication and authorization on the HTTPS connection and then redirect to HTTP for the GET or PUT file transfer. htcp makes the HTTP request using -the GRID_AUTH_ONETIME single-use passcode obtained via HTTPS. The downgrade -option will be ignored for directory operations, HTTP URLs, or if the file -size is less than the value given. If a downgraded transfer isn't possible, -a normal HTTPS data transfer will be done. +the GRID_AUTH_ONETIME single-use passcode obtained via HTTPS. The --grid-http +option will be ignored for directory operations or HTTP URLs. If a redirected +transfer isn't possible, a normal HTTPS data transfer will be attempted. + +.IP "--sitecast" +Try to use SiteCast to locate remote files which are to be copied (currently +only for the +.BR fetching +of remote files.) If no location is found via SiteCast, then a direct request +for the given URL is tried. (--groups must be used for this option to work.) + +.IP "--domain " +Try to use SiteCast to locate remote files which are to be copied (currently +only for the +.BR fetching +of remote files) +.BR "if the domain component of the URL matches" +the SiteCast domain given. +If no location is found via SiteCast, then a direct request +for the given URL is tried. (--groups must be used for this option to work.) .SH FILES .IP /tmp/x509up_uID @@ -130,13 +185,11 @@ is returned when the HTTP(S) server returns a code outside the range 200-299. The manpage libcurl-errors(3) lists all the curl error codes. .SH TO DO -Recursive copying. Server-side wildcards. Parallel streams. Error recovery. - -.SH BUGS -Not enough beta testing (hint hint...) +Recursive copying. Server-side wildcards. Parallel streams. Better error +recovery. .SH AUTHOR -Andrew McNab +Andrew McNab htcp is part of GridSite: http://www.gridsite.org/ .SH "SEE ALSO" diff --git a/org.gridsite.core/doc/htfind.1 b/org.gridsite.core/doc/htfind.1 new file mode 100644 index 0000000..11a60d1 --- /dev/null +++ b/org.gridsite.core/doc/htfind.1 @@ -0,0 +1 @@ +.so man1/htcp.1 diff --git a/org.gridsite.core/doc/htmv.1 b/org.gridsite.core/doc/htmv.1 new file mode 100644 index 0000000..11a60d1 --- /dev/null +++ b/org.gridsite.core/doc/htmv.1 @@ -0,0 +1 @@ +.so man1/htcp.1 diff --git a/org.gridsite.core/doc/htping.1 b/org.gridsite.core/doc/htping.1 new file mode 100644 index 0000000..11a60d1 --- /dev/null +++ b/org.gridsite.core/doc/htping.1 @@ -0,0 +1 @@ +.so man1/htcp.1 diff --git a/org.gridsite.core/doc/httpd-fileserver.conf b/org.gridsite.core/doc/httpd-fileserver.conf index 5e1196b..301a8ee 100644 --- a/org.gridsite.core/doc/httpd-fileserver.conf +++ b/org.gridsite.core/doc/httpd-fileserver.conf @@ -1,10 +1,14 @@ ############################################################################## ## GridSite httpd-fileserver.conf - Andrew McNab ## -## Example configuration file for GridSite as an HTTP(S) fileserver. -## ## For GridSite documentation, see http://www.gridsite.org/ ## +## Example configuration file for GridSite as an HTTP(S) fileserver, +## listening on ports 80/777 (HTTP) and 443/488 (HTTPS) +## +## (777/488 is to allow firewalls to distinguish between Grid and +## Web HTTP(S) traffic. See http://www.gridsite.org/wiki/IP_Ports ) +## ## This file should be renamed /etc/httpd/conf/httpd.conf and Apache ## restarted to use Apache2/GridSite as a simple HTTP(S) fileserver. ## @@ -23,7 +27,7 @@ ## in /etc/grid-security/dn-lists/ ## ## To start serving files, make a directory /var/www/htdocs owned by -## nobody.nobody, including the file .gacl containing: +## apache.apache, including the file .gacl containing: ## ## ## @@ -50,7 +54,7 @@ ## ## and add the following directive to the HTTPS section: ## -## GridSiteMethods GET PUT DELETE +## GridSiteMethods GET PUT DELETE MOVE ## ## If you wish to accept Globus GSI Proxies as well as full X.509 user ## certificates, set GridSiteGSIProxyLimit to the depth of proxy you @@ -67,6 +71,7 @@ ## ## (or with --cert /tmp/x509up_u`id -u` --key /tmp/x509up_u`id -u` to use ## a Globus GSI Proxy created with grid-proxy-init.) +## ############################################################################## ServerRoot "/etc/httpd" @@ -87,8 +92,8 @@ LoadModule dir_module /usr/lib/httpd/modules/mod_dir.so TypesConfig /etc/mime.types # User and group who will own files created by Apache -User nobody -Group nobody +User apache +Group apache DocumentRoot "/var/www/htdocs" @@ -105,11 +110,12 @@ ErrorLog logs/httpd-gridsite-errors HostnameLookups On ###################################################################### -# Plain unauthenticated HTTP on port 80 +# Plain unauthenticated HTTP on ports 80 and 777 ###################################################################### Listen 80 - +Listen 777 + GridSiteIndexes on @@ -120,13 +126,14 @@ Listen 80 ###################################################################### -# Secured and possibly authenticated HTTPS on port 443 +# Secured and possibly authenticated HTTPS on ports 443 and 488 ###################################################################### Listen 443 +Listen 488 SSLSessionCacheTimeout 300 -SSLSessionCache dbm:/var/cache/mod_ssl/scache -# This version of GridSite is NOT compatible with the SHM SSL cache!!! - +SSLSessionCache shm:/var/cache/mod_ssl/shm_cache + + SSLEngine on SSLCertificateFile /etc/grid-security/hostcert.pem @@ -142,7 +149,7 @@ SSLOptions +ExportCertData +StdEnvVars GridSiteAuth on GridSiteDNlists /etc/grid-security/dn-lists/ GridSiteGSIProxyLimit 0 -# GridSiteMethods GET PUT DELETE +# GridSiteMethods GET PUT DELETE MOVE diff --git a/org.gridsite.core/doc/httpd-webserver.conf b/org.gridsite.core/doc/httpd-webserver.conf index 6919c9b..dfb3edb 100644 --- a/org.gridsite.core/doc/httpd-webserver.conf +++ b/org.gridsite.core/doc/httpd-webserver.conf @@ -1,10 +1,14 @@ ############################################################################## ## GridSite httpd-webserver.conf - Andrew McNab ## +## For GridSite documentation, see http://www.gridsite.org/ +## ## Example configuration file for GridSite as a Web Server ## (that is, primarily for interactive use with a browser.) +## Listening is on ports 80/777 (HTTP) and 443/488 (HTTPS). ## -## For GridSite documentation, see http://www.gridsite.org/ +## (777/488 is to allow firewalls to distinguish between Grid and +## Web HTTP(S) traffic. See http://www.gridsite.org/wiki/IP_Ports ) ## ## This file should be renamed /etc/httpd/conf/httpd.conf and Apache ## restarted to use Apache2/GridSite as a webserver. @@ -25,7 +29,7 @@ ## (Lists in /etc/grid-security/dn-lists/ override lists elsewhere.) ## ## To start serving files, make a directory /var/www/htdocs owned by -## nobody.nobody, including the file .gacl containing: +## apache.apache, including the file .gacl containing: ## ## ## @@ -52,7 +56,7 @@ ## ## and add the following directive to the HTTPS section: ## -## GridSiteMethods GET PUT DELETE +## GridSiteMethods GET PUT DELETE MOVE ## ## If you wish to accept Globus GSI Proxies as well as full X.509 user ## certificates, set GridSiteGSIProxyLimit to the depth of proxy you @@ -95,8 +99,8 @@ LoadModule cgi_module /usr/lib/httpd/modules/mod_cgi.so TypesConfig /etc/mime.types # User and group who will own files created by Apache -User nobody -Group nobody +User apache +Group apache DocumentRoot "/var/www/htdocs" @@ -113,11 +117,12 @@ ErrorLog logs/httpd-gridsite-errors HostnameLookups On ###################################################################### -# Plain unauthenticated HTTP on port 80 +# Plain unauthenticated HTTP on ports 80 and 777 ###################################################################### Listen 80 - +Listen 777 + ## This is used to serve the Manage Directory links in footers, ## and to allow you to edit files and ACLs via your browser. @@ -150,13 +155,14 @@ ScriptAlias /real-gridsite-admin.cgi /usr/sbin/real-gridsite-admin.cgi ###################################################################### -# Secured and possibly authenticated HTTPS on port 443 +# Secured and possibly authenticated HTTPS on ports 443 and 488 ###################################################################### Listen 443 +Listen 488 SSLSessionCacheTimeout 300 -SSLSessionCache dbm:/var/cache/mod_ssl/scache -# This version of GridSite is NOT compatible with the SHM SSL cache!!! - +SSLSessionCache shm:/var/cache/mod_ssl/shm_cache + + SSLEngine on SSLCertificateFile /etc/grid-security/hostcert.pem @@ -205,7 +211,7 @@ ScriptAlias /real-gridsite-admin.cgi /usr/sbin/real-gridsite-admin.cgi ## This directive allows authorized people to write/delete files ## from non-browser clients - eg with htcp(1) - GridSiteMethods GET PUT DELETE + GridSiteMethods GET PUT DELETE MOVE ## These directives (and the ScriptAlias above) allow authorized ## people to manage files, ACLs and DN Lists through their web diff --git a/org.gridsite.core/doc/index.html b/org.gridsite.core/doc/index.html index a93f2cb..d182ead 100644 --- a/org.gridsite.core/doc/index.html +++ b/org.gridsite.core/doc/index.html @@ -11,29 +11,35 @@ the associated verified credentials are available to all technologies supported by Apache, including static file serving, SSI, CGI, PHP, JSP and mod_perl. -

Guides

+

+The GridSite Wiki includes +guides and cookbook examples about using GridSite, along with up to date +information about the APIs. + +

Reference

-

-
User Guide -
End-user documentation for people managing webpages and files on - GridSite servers, either through the web interface or with command - line clients like htcp. +The following reference documents and man pages are put in +/usr/share/doc/gridsite-VERSION when GridSite is installed. +

+

-
Admin Guide -
For people administering areas of GridSite websites or fileservers, or - managing GridSite's support for DN List groups. +
htcp(1) +
A command line tool for copying files to or from HTTP(S) servers.

-

Building and Installation -
Instructions for building GridSite from source, and installing from - binaries or RPMs. +
mod_gridsite(8) +
An Apache 2.0 module which enforces access control via Grid Access + Control Lists, and X.509, GSI or VOMS credentials. mod_gridsite also + gives Apache built-in support for the HTTP PUT and DELETE methods, and + formatting of HTML pages with standard headers and footers.

-

Config Guide -
For webmasters setting up Apache 2.0 and GridSite, and writing the - Apache httpd.conf file. +
gsexec(8) +
A modified version of suexec(8), for use with mod_gridsite(8). gsexec + allows CGI programs to be run as pool users, depending on the client's + X.509 identity or the directory in which the CGI is located.

httpd-fileserver.conf and @@ -42,22 +48,21 @@ mod_perl. webservers, with explanatory comments.

-

- -

Reference

- -

-

-
Grid Access Control Lists -
Syntax and usage of the XML Grid Access Control Lists used by GridSite. +
urlencode(1) +
A command for URL-encoding strings.

-

htcp and - urlencode man pages -
Command line tools for copying files to or from HTTP(S) servers, and - for URL-encoding strings. +
findproxyfile(1) +
The finxproxyfile command returns full path to a GSI Proxy file, + either in the proxy cache maintained by the GridSite G-HTTPS and + delegation portType functions, or in other standard places.

+

delegation-1.wsdl +
A WSDL description of a delegation Web Service including the Delegation + portType. +

+ -

mod_gridsite -
An Apache 2.0 module which enforces access control via Grid Access - Control Lists, and X.509, GSI or VOMS credentials. mod_gridsite also - gives Apache built-in support for the HTTP PUT and DELETE methods, and - formatting of HTML pages with standard headers and footers. -

- - - -

gridsite.h API reference +
gridsite.h API reference
A detailed description of the C API provided by libgridsite, generated from the sources by doxygen.

diff --git a/org.gridsite.core/doc/install.html b/org.gridsite.core/doc/install.html deleted file mode 100644 index a5845de..0000000 --- a/org.gridsite.core/doc/install.html +++ /dev/null @@ -1,158 +0,0 @@ -GridSite: Building and Installation Guide - -

GridSite: Building and Installation Guide

- -

-This Guide explains how to build GridSite from source, and how to install -the server components alongside an Apache 2.0 webserver. There is a -separate Config Guide which explains how to modify -the httpd.conf file, and how to set up other files and directories used by -the system. You should look through all of this Building and Installation -Guide to decide which is the easiest route for your system. - -

Installing with RPM

- -

-If you are installing on Linux with a binary RPM release, you can skip -most of this Guide, install the binary rpm(s) and go straight -to the Config Guide. - - - -

-RedHat 9, Fedora, RHEL, Scientific Linix: -This is the simpler case, since the standard release includes a suitable -version of Apache 2.0: just install the gridsite-...-1.i386.rpm to get the -various GridSite components. - -

-Earlier, eg RedHat 7.3: -This is more complicated because you must also install a back-ported Apache -2.0 RPM or build it from source. - - -

-GridSite also depends on shared libraries from libcurl and libxml2, and the -RPMs distributed as part of the standard RedHat, from 7.3 onwards, are -sufficient. - -

-With the RPMs installed, you can proceed to the -Config Guide. - -

Requirements for building GridSite from source

- -

-GridSite is currently only supported on Linux, but should be -straightforwardly -portable to other Unix platforms where the GNU build tools are available. - -

-GridSite consists of a core library (libgridsite[.so|.a]), an Apache module -(mod_gridsite.so), a CGI utility (gridsite-admin.cgi) and some command line -tools (htcp, urlencode.) - -

-All of the components use the GridSite library, and this in turn depends on -libcurl and libxml2. You will need the development versions of these -packages installed before you can proceed. - - -

Building GridSite with Make

- -

-Our download area at - -https://www.gridsite.org/download/ includes a tar-ball -distribution of the sources, which can be unpacked and used to build -GridSite from source. (Bleeding-edge developers can get the current snapshot -of the same files from our CVS area.) - -

-GridSite needs a copy of the Apache 2.0 include files to build, and the -location of this is set by the MYCFLAGS variable in the top-level Makefile. -For manual builds, the default -MYCFLAGS=-I/usr/local/include/httpd is used. -If you wish to use the GridSite module with Apache -2.0 installed elsewhere, you should change the MYCFLAGS variable to point to -the includes directory installed by the development part of that Apache 2.0 -distribution. - -

-

-make 
-make install
-
- -

-will build all components and install them all under the default -locations of /usr/local/[lib|bin|include|sbin] The default prefix for manual -builds is -/usr/local, as set by the prefix variable in the top level Makefile -(/usr is the default for RPMs.) - -

Building GridSite with RPM

- -

-For RedHat Linux and derivatives, building with RPM is recommended. -The command make rpm in the top level of the source tree -will build the GridSite and htcp binary RPMs in the -directory ../RPMTMP/RPMS/i386 relative to the working directory. An SRPM is -put into ../RPMTMP/SRPMS -This build assumes the Apache 2.0 includes are in /usr/include/httpd. - - - -

-For other configurations, -you can modify the assumed location of the Apache 2.0 includes -by changing the MYCFLAGS variable in the rpm target near the -foot of the top level Makefile. - -

Building Apache 2.0

- -

-If it is not possible to use binary RPMs of Apache 2.0, -then it can be built from source using the build-apache2.sh script -found in the GridSite docs directory. -The script includes instructions on how to build from the tarballs -distributed by the Apache Foundation. -(it removes the -C option from "configure -C" in the .spec file -and builds the RPMs under the current directory.) - -

-If these targets do not work on your build platform, -the Makefile and the scriptlets in the included SPEC files are a good -starting point for building Apache by hand yourself. The complexities of -this are outside of the scope of this Guide, but you are welcome to ask for -assistance on the -GridSite -Discussion List, although -www.apache.org is a better starting -point for purely Apache problems. - - diff --git a/org.gridsite.core/doc/library.html b/org.gridsite.core/doc/library.html deleted file mode 100644 index 28458ae..0000000 --- a/org.gridsite.core/doc/library.html +++ /dev/null @@ -1 +0,0 @@ -library docs diff --git a/org.gridsite.core/doc/mod_gridsite.8 b/org.gridsite.core/doc/mod_gridsite.8 new file mode 100644 index 0000000..1577633 --- /dev/null +++ b/org.gridsite.core/doc/mod_gridsite.8 @@ -0,0 +1,311 @@ +.TH MOD_GRIDSITE 8 "October 2005" "mod_gridsite" "GridSite Manual" +.SH NAME +.B mod_gridsite +\- Grid extensions to Apache httpd +.SH SYNOPSIS +.B LoadModule gridsite_module mod_gridsite.so +.SH DESCRIPTION +.B mod_gridsite +is an Apache 2.0 module which enforces access control via Grid +Access Control Lists, and X.509, GSI or VOMS credentials. mod_gridsite also +gives Apache built-in support for the HTTP PUT and DELETE methods, and +formatting of HTML pages with standard headers and footers. + +Since mod_gridsite access +control within Apache itself, Grid authorization and +the associated verified credentials are available to all technologies +supported by Apache, including static file serving, SSI, CGI, PHP, mod_perl +and Java servlets via a connector to Tomcat. + +Operation of mod_gridsite can be configured using runtime directives +in Apache's standard httpd.conf configuration file. The module must first be +loaded with a LoadModule directive: + +LoadModule gridsite_module /PATH/TO/MODULES/mod_gridsite.so + +The module's behaviour is then controlled by GridSite... directives within +Apache sections, allowing different directories to use +GridSite features in different ways. + +.SH DIRECTIVES + +.IP "GridSiteIndexes on|off" +Determines whether GridSite generates HTML directory listings. These +have some advantages over standard Apache directory listings (eg the +displayed filenames are never truncated) and will include standard +headers and footers if GridSiteHtmlFormat is on. +(Default: GridSiteIndexes off) + +.IP "GridSiteIndexHeader file" +If the named file is found in the directory being listed, the file +is included verbatim at the top of the listing and excluded from +the file-by-file listing. The file can either be HTML or plain text (in +which case browsers will be treat it as one HTML paragraph.) +(Default: none) + +.IP "GridSiteHtmlFormat on|off" +Determines where HTML pages receive additional formatting before being +sent to the client. This includes the "Last modified", +"View page history", "Switch to HTTP(S)", +"Print View" and "Built with GridSite" footer +elements. If header and footer files are found, they will be used too. +(Default: GridSiteHtmlFormat off) + +.IP "GridSiteHeadFile file" +.IP "GridSiteFootFile file" +Set the filenames to be searched for as standard headers and footers +for HTML pages. For each HTML page, the directory of that page is tried +first, and then parent directories in ascending order until a header / +footer file is found. Header files are inserted in place of HTML + tags; footer files in place of . (These +standard files should each include the appropriate body tag as a +replacement.) +(Defaults: GridSiteHeadFile gridsitehead.txt, +GridSiteFootFile gridsitefoot.txt) + +.IP "GridSiteAuth on|off" +Enables GridSite access control features, using +GACL files. The files are named .gacl and are +per-directory. The current directory is tried and then parent +directories in ascending order until a .gacl file is found. +(Default: GridSiteAuth off) + +.IP "GridSiteAdminList uri" +All members of the DN List with name "uri" receive the full set +of permissions, irrespective of per-directory .gacl files. People in +this group have full control over the whole site. +(Default: none) + +.IP "GridSiteGSIProxyLimit limit" +When using GSI Proxy credentials, +proxies with delegation depth greater than "limit" will +be ignored by mod_gridsite authorization decisions. A limit of zero +implies only full X.509 +certificates (and no proxies) will be accepted. A limit of 1 implies +that only the initial proxy, usually created on the user's own machine, +is acceptable. Higher levels lead to proxies on remote machines, eg +used by running jobs, being accepted. +(Default: GridSiteGSIProxyLimit 1) + +.IP "GridSiteMethods [GET] [PUT] [DELETE] [MOVE]" +Specifies which HTTP methods are supported by GridSite. GET (and HEAD) +are always supported. PUT and DELETE support is turned on by this +directive, subject to a positive statement that write permission is +allowed for the directory in question, by a GACL file. +(Default: GridSite GET) + +.IP "GridSiteDNlists directory1[:directory2[:directory3]...]" +Sets up the DN List path used by GACL for +evaluating credentials. If this directive is not used, +then GACL will use the GRST_DN_LISTS variable from Apache's own +environment. If that is not set either, then /etc/grid-security/dn-lists +is searched. +(Default: none) + +.IP "GridSiteDNlistsURI uri" +If GridSiteDNlistsURI is used, then the URI given appears to be +populated with all the DN lists on the current DN lists path which +match the current server. That is, for server https://example.org/ +with DN lists URI /dn-lists/, all DN lists with URLs starting +https://example.org/dn-lists/ will appear to be present in /dn-lists/, +irrespective of where in the path they are stored. +(Default: none) +

+ +.IP "GridSiteAdminURI uri" +GridSiteAdminURI gives the absolute URI on the server of the GridSite +Admin CGI program, which is used for file management, HTML and GACL +editing. This should be used in conjunction with the standard Apache +directive ScriptAlias to map that URI to the real-gridsite-admin.cgi +executable. For example: + +ScriptAlias /real-gridsite-admin.cgi /PATH/TO/real-gridsite-admin.cgi + +This URI is always reached by an internal redirection from the value +set by GridSiteAdminFile, and is never visible to users. +(Default: none) + +.IP "GridSiteAdminFile cgifilename" +If GridSiteAdminURI is set, then the cgifilename of GridSiteAdminFile +appears to be present in all directories when explicitly +requested (it does not appear in directory listings.) Requests for these +ghost CGI URIs are internally redirected to the value set by +GridSiteAdminURI. (Default: GridSiteAdminFile gridsite-admin.cgi) + +.IP "GridSiteEnvs on|off" +This makes mod_gridsite export several variables into the environment +of CGI programs and other dynamic content systems. The variable names +are listed below. For gridsite-admin.cgi mechanism to work, this switch +must be left in its default state of on. +(Default: GridSiteEnvs on) + +.IP "GridSiteEditable [ext1 [ext2 [ext3] ...]]]" +A space-separated list of file extensions which can safely be edited +by the GridSite Text/HTML editor. The extensions are given without the +initial dot. +(Default: GridSiteEditable txt shtml html htm css js php jsp) + +.IP "GridSiteHelpURI uri" +If set, gives the URI to use for "Website Help" links in HTML +page footers. (Default: none) + +.IP "GridSiteLink on|off" +Turns off the link in the HTML page footers which gives credit to GridSite. +(Default: GridSiteLink on) + +.IP "GridSiteUnzip path" +If "path" is set by this directive, then real-gridsite-admin.cgi +will offer to list the contents of .zip archives on the server. +Users with write access are able to unpack the contents into the same +directory as the .zip file. The value of "path" must point +to the location of the unzip binary. (Default: none) + +.IP "GridSiteGridHTTP on|off" +Enable GridHTTP for this server, virtual server or directory: +HTTPS requests made with the header +.BR "Upgrade: GridHTTP/1.0" +will be redirected to an HTTP version of the file. (Default: off) + +.IP "GridSiteGridHTTPport port" +Sets the port to use for the unencrypted HTTP component of GridHTTP +HTTPS->HTTP transfers. The same setting will be used for all virtual hosts +which support GridHTTP. (Default: 777) + +.IP "GridSiteOnetimesDir path" +Location of authentication cookies directory, relative to ServerRoot. +Used by GridHTTP to record the credentials obtained via HTTPS, +and available to the corresponding HTTP request. (Default: /var/www/onetimes) + +.IP "GridSiteACLFormat GACL|XACML" +Format to use when writing .gacl files. (Both formats are automatically +recognised when reading.) (Default: GACL) + +.IP "GridSiteExecMethod nosetuid|suexec|X509DN|directory" +Execution strategy for CGI scripts and executables. For options other +than nosetuid, suexec (or gsexec renamed suexec) must installed. For +X509DN and directory, gsexec must be installed, as suexec. See +.BR "gsexec(8)" +for an explanation of the different execution strategies. +(Default: nosetuid) + +.IP "GridSiteUserGroup user group" +Unix user and group when using suexec (or gsexec as suexec.) This +is equivalent to the suexec SuexecUserGroup directive, but can be +specified on a per-directory basis. (Default: none) + +.IP "GridSiteDiskMode GroupNone|GroupRead|GroupWrite WorldNone|WorldRead" +The file creation permissions mode, taking two arguments to specify +the group and other permissions. The mode always includes read and write +permission for the CGI user itself. +(Default: GroupNone WorldNone) + +.IP "GridSiteCastUniPort port" +The +.BR UDP +unicast port to listen on for HTCP queries, and from which to +send replies to HTCP unicast and multicast queries. Ideally, this should be +a privileged port below 1024. This directive may not appear within a virtual +server. (Default: 777) + +.IP "GridSiteCastGroup group[:port]" +A UDP multicast group on which to listen for HTCP queries, plus an optional +port. If no port is given, then 777 is used. Multiple GridSiteCastGroup +directives can be given to cause the UDP responder to listen to more than +one multicast group. This directive may not appear within a virtual server. + +.IP "GridSiteCastAlias URL-prefix path-prefix" +Maps SiteCast generic URLs to the local filesystem. When processing +HTCP queries, matching SiteCast URLs will have URL-prefix stripped off +and the remaining portion of the URL added to path-prefix to construct a +local path and filename. If a file is found with that name, a SiteCast HTCP +response will be returned to the querying host. Otherwise the queries are +ignored. +This directive may appear within virtual servers, and the virtual server's +servername and first port will determine the host and port name used to +construct the transfer URL. + +.SH ENVIRONMENT + +The following variables are present in the environment of CGI programs and +other dynamic content systems if the +.BR "GridSiteEnvs on" +directive is in effect. + +.IP GRST_PERM +Numerical value of the permission bit-map obtained by comparing the +user with the GACL in force. (These should be tested using the +GRSTgaclPermHasXXXX functions from GACL.) + +.IP GRST_ADMIN_LIST +URI of the DN List, listing people with full admin and write access +to the whole site. + +.IP GRST_GSIPROXY_LIMIT +Maximum valid delegation level for GSI Proxies. + +.IP GRST_DIR_PATH +Absolute path in the local filesystem to the directory holding the +file being requested. + +.IP GRST_DESTINATION_TRANSLATED +Present if a WebDAV +.BR "Destination:" +header was given in the request with a local URL. Contains the translation of +the URL given into an absolute path in the local filesystem. + +.IP GRST_HELP_URI +URI of website help pages set by GridSiteHelpURI directive. + +.IP GRST_ADMIN_FILE +Filename of per-directory ghost gridsite-admin.cgi program. (This is +used by real-gridsite-admin.cgi to construct links in its pages.) + +.IP GRST_EDITABLE +Space-separated list of extensions which can safely be edited with a +Text/HTML editor. + +.IP "GRST_HEAD_FILE and GRST_FOOT_FILE" +Filenames of standard header and footer files. + +.IP GRST_DN_LISTS +DN lists search path. + +.IP GRST_DN_LISTS_URI +Directory of virtual URIs used to publish this site's DN Lists. + +.IP GRST_UNZIP +Full path to the +.BR "unzip(1)" +binary, used to list and unpack .zip files. + +.IP GRST_NO_LINK +If set, do not include credit links to GridSite in page footers. + +.IP GRST_ACL_FORMAT +Format to use when writing .gacl files: either GACL or XACML. + +.IP GRST_EXEC_METHOD +Specified by +.BR GridSiteExecMethod +either suexec, X509DN or directory. + +.IP GRST_EXEC_DIRECTORY +The directory containing the CGI script or executable (used by gsexec +to determine which pool account to use in directory mapping mode.) + +.IP GRST_DISK_MODE +The +.BR Apache +disk permission modes bit pattern, in hexadecimal, starting with 0x. +(Similar to the Unix bit pattern, except with hexadecimal rather than +octal values: eg 0x600 [Apache] vs 0600 [Unix] +are both read/write for user only.) + +.SH AUTHOR +Andrew McNab + +mod_gridsite is part of GridSite: http://www.gridsite.org/ +.SH "SEE ALSO" +.BR htcp(1), +.BR httpd(8), +.BR gsexec(8) diff --git a/org.gridsite.core/doc/module.html b/org.gridsite.core/doc/module.html deleted file mode 100644 index 9cc97d4..0000000 --- a/org.gridsite.core/doc/module.html +++ /dev/null @@ -1,350 +0,0 @@ -GridSite Apache module: mod_gridsite - -

GridSite Apache module: mod_gridsite

- -

-mod_gridsite is an Apache 2.0 module which enforces access control via Grid -Access Control Lists, and X.509, GSI or VOMS credentials. mod_gridsite also -gives Apache built-in support for the HTTP PUT and DELETE methods, and -formatting of HTML pages with standard headers and footers. - -

-Since mod_gridsite access -control within Apache itself, Grid authorization and -the associated verified credentials are available to all technologies -supported by Apache, including static file serving, SSI, CGI, PHP, mod_perl -and Java servlets via a connector to Tomcat. - -

-Operation of mod_gridsite can be configured using runtime directives -in Apache's standard httpd.conf configuration file. The module must first be -loaded with a LoadModule directive: - -

-LoadModule gridsite_module /PATH/TO/MODULES/mod_gridsite.so - -

-The module's behaviour is then controlled by GridSite... directives within -Apache <Directory ...> sections, allowing different directories to use -GridSite features in different ways. - -

GridSite directives

- -
-
GridSiteIndexes on|off -
Determines whether GridSite generates HTML directory listings. These - have some advantages over standard Apache directory listings (eg the - displayed filenames are never truncated) and will include standard - headers and footers if GridSiteHtmlFormat is on. -
- (Default: GridSiteIndexes off) -

- -

GridSiteIndexHeader file -
If the named file is found in the directory being listed, the file - is included verbatim at the top of the listing and excluded from - the file-by-file listing. The file can either be HTML or plain text (in - which case browsers will be treat it as one HTML paragraph.) -
- (Default: none) -

- -

GridSiteHtmlFormat on|off -
Determines where HTML pages receive additional formatting before being - sent to the client. This includes the "Last modified", - "View page history", "Switch to HTTP(S)", - "Print View" and "Built with GridSite" footer - elements. If header and footer files are found, they will be used too. -
- (Default: GridSiteHtmlFormat off) -

- -

GridSiteHeadFile file
- GridSiteFootFile file -
Set the filenames to be searched for as standard headers and footers - for HTML pages. For each HTML page, the directory of that page is tried - first, and then parent directories in ascending order until a header / - footer file is found. Header files are inserted in place of HTML - <body[ ...]> tags; footer files in place of </body>. (These - standard files should each include the appropriate body tag as a - replacement.) -
- (Defaults: GridSiteHeadFile gridsitehead.txt, - GridSiteFootFile gridsitefoot.txt) -

- -

GridSiteAuth on|off -
Enables GridSite access control features, using - GACL files. The files are named .gacl and are - per-directory. The current directory is tried and then parent - directories in ascending order until a .gacl file is found. -
- (Default: GridSiteAuth off) -

- -

GridSiteAdminList uri -
All members of the DN List with name "uri" receive the full set - of permissions, irrespective of per-directory .gacl files. People in - this group have full control over the whole site. -
- (Default: none) -

- -

GridSiteGSIProxyLimit limit -
When using GSI Proxy credentials, - proxies with delegation depth greater than "limit" will - be ignored by mod_gridsite authorization decisions. A limit of zero - implies only full X.509 - certificates (and no proxies) will be accepted. A limit of 1 implies - that only the initial proxy, usually created on the user's own machine, - is acceptable. Higher levels lead to proxies on remote machines, eg - used by running jobs, being accepted. -
- (Default: GridSiteGSIProxyLimit 1) -

- -

GridSiteMethods [GET] [PUT] [DELETE] -
Specifies which HTTP methods are supported by GridSite. GET (and HEAD) - are always supported. PUT and DELETE support is turned on by this - directive, subject to a positive statement that write permission is - allowed for the directory in question, by a GACL file. -
- (Default: GridSite GET) -

- -

GridSiteDNlists directory1[:directory2[:directory3]...] -
Sets up the DN List path used by GACL for - evaluating <dn-list> credentials. If this directive is not used, - then GACL will use the GRST_DN_LISTS variable from Apache's own - environment. If that is not set either, then /etc/grid-security/dn-lists - is searched. -
- (Default: none) -

- -

GridSiteDNlistsURI uri -
If GridSiteDNlistsURI is used, then the URI given appears to be - populated with all the DN lists on the current DN lists path which - match the current server. That is, for server https://example.org/ - with DN lists URI /dn-lists/, all DN lists with URLs starting - https://example.org/dn-lists/ will appear to be present in /dn-lists/, - irrespective of where in the path they are stored. -
- (Default: none) -

- -

GridSiteAdminURI uri -
GridSiteAdminURI gives the absolute URI on the server of the GridSite - Admin CGI program, which is used for file management, HTML and GACL - editing. This should be used in conjunction with the standard Apache - directive ScriptAlias to map that URI to the real-gridsite-admin.cgi - executable. For example: -
- ScriptAlias /real-gridsite-admin.cgi - /PATH/TO/real-gridsite-admin.cgi -
- This URI is always reached by an internal redirection from the value - set by GridSiteAdminFile, and is never visible to users. -
- (Default: none) -

- -

GridSiteAdminFile cgifilename -
If GridSiteAdminURI is set, then the cgifilename of GridSiteAdminFile - appears to be present in all directories when explicitly - requested (it does not appear in directory listings.) Requests for these - ghost CGI URIs are internally redirected to the value set by - GridSiteAdminURI. -
- (Default: GridSiteAdminFile gridsite-admin.cgi) -

- -

GridSiteEnvs on|off -
This makes mod_gridsite export several variables into the environment - of CGI programs and other dynamic content systems. The variable names - are listed below. For gridsite-admin.cgi mechanism to work, this switch - must be left in its default state of on. -
- (Default: GridSiteEnvs on) -

- -

GridSiteEditable [ext1 [ext2 [ext3] ...]]] -
A space-separated list of file extensions which can safely be edited - by the GridSite Text/HTML editor. The extensions are given without the - initial dot. -
- (Default: GridSiteEditable txt shtml html htm css js php jsp) -

- -

GridSiteHelpURI uri -
If set, gives the URI to use for "Website Help" links in HTML - page footers. -
- (Default: none) -

- -

GridSiteLink on|off -
Turns off the link in the HTML page footers which gives credit to - GridSite. -
- (Default: GridSiteLink on) -

- -

GridSiteUnzip path -
If "path" is set by this directive, then real-gridsite-admin.cgi - will offer to list the contents of .zip archives on the server. - Users with write access are able to unpack the contents into the same - directory as the .zip file. The value of "path" must point - to the location of the - unzip binary. -
- (Default: none) -

- -

GridSiteDowngrade on|off -
Enable HTTPS Downgrade for this server, virtual server or directory: - HTTPS requests made with the header HTTP-Downgrade-Size: - will be redirected to an HTTP version of the file, unless the file is - smaller than the given size. -
- (Default: off) -

- -

GridSiteAuthCookiesDir path -
Location of authentication cookies directory, relative to ServerRoot. - Used by HTTPS Downgrade to record the credentials obtained via HTTPS, - and available to the corresponding HTTP request. -
- (Default: gridauthcookies) -

- -

GridSiteACLFormat GACL|XACML -
Format to use when writing .gacl files. (Both formats are automatically - recognised when reading.) -
- (Default: GACL) -

- -

GridSiteExecMethod nosetuid|suexec|X509DN|directory -
Execution strategy for CGI scripts and executables. For options other - than nosetuid, suexec (or gsexec renamed suexec) must installed. For - X509DN and directory, gsexec must be installed, as suexec. -
- With X509DN, the CGI process runs as a pool user, detemined using lock - files in the pool mapping directory chosen as build time of gsexec. - (/var/www/execmapdir by default.) The pool user is chosen according - to the client's full certificate X.509 DN (ie without any GSI proxy - name components.) -
- With directory, the CGI process runs as a pool user chosen according - to the directory in which the CGI is located: all CGIs in that directory - run as the same pool user. -
- (Default: nosetuid) -

- -

GridSiteUserGroup user group -
Unix user and group when using suexec (or gsexec as suexec.) This - is equivalent to the suexec SuexecUserGroup directive, but can be - specified on a per-directory basis. -
- (Default: none) -

- -

GridSiteDiskMode GroupNone|GroupRead|GroupWrite WorldNone|WorldRead -
The file creation permissions mode, taking two arguments to specify - the group and other permissions. The mode always includes read and write - permission for the CGI user itself. -
- (Default: GroupNone WorldNone) -

- -

- -

Environment variables

- -

-The following variables are present in the environment of CGI programs and -other dynamic content systems if the GridSiteEnvs on directive is -in effect. - -

-

-
GRST_PERM -
Numerical value of the permission bit-map obtained by comparing the - user with the GACL in force. (These should be tested using the - GRSTgaclPermHasXXXX functions from GACL.) -

- -

GRST_ADMIN_LIST -
URI of the DN List, listing people with full admin and write access - to the whole site. -

- -

GRST_GSIPROXY_LIMIT -
Maximum valid delegation level for GSI Proxies. -

- -

GRST_DIR_PATH -
Absolute path in the local filesystem to the directory holding the - file being requested. -

- -

GRST_HELP_URI -
URI of website help pages set by GridSiteHelpURI directive. -

- -

GRST_ADMIN_FILE -
Filename of per-directory ghost gridsite-admin.cgi program. (This is - used by real-gridsite-admin.cgi to construct links in its pages.) -

- -

GRST_EDITABLE -
Space-separated list of extensions which can safely be edited with a - Text/HTML editor. -

- -

GRST_HEAD_FILE and GRST_FOOT_FILE -
Filenames of standard header and footer files. -

- -

GRST_DN_LISTS -
DN lists search path. -

- -

GRST_DN_LISTS_URI -
Directory of virtual URIs used to publish this site's DN Lists. -

- -

GRST_UNZIP -
Full path to the unzip binary, used to list and unpack .zip files. -

- -

GRST_NO_LINK -
If set, do not include credit links to GridSite in page footers. -

- -

GRST_ACL_FORMAT -
Format to use when writing .gacl files: either GACL or XACML. -

- -

GRST_EXEC_METHOD -
Specified by GridSiteExecMethod, either suexec, X509DN or directory. -

- -

GRST_EXEC_DIRECTORY -
The directory containing the CGI script or executable (used by gsexec - to determine which pool account to use in directory mapping mode.) -

- -

GRST_DISK_MODE -
The Apache disk permission modes bit pattern, in hexadecimal, - starting with 0x. - (Similar to the Unix bit pattern, except with hexadecimal rather than - octal values: eg 0x600 [Apache] vs 0600 [Unix] - are both read/write for user only.) -

- -

- - diff --git a/org.gridsite.core/doc/urlencode.1 b/org.gridsite.core/doc/urlencode.1 index fe84405..7cdfbc3 100644 --- a/org.gridsite.core/doc/urlencode.1 +++ b/org.gridsite.core/doc/urlencode.1 @@ -1,4 +1,4 @@ -.TH urlencode 1 "November 2003" urlencode "URLENCODE Manual" +.TH URLENCODE 1 "November 2003" "urlencode" "GridSite Manual" .SH NAME .B urlencode \- convert strings to or from URL-encoded form @@ -37,10 +37,7 @@ with the exception that + is converted to space. .SH EXIT CODES 0 is always returned. -.SH BUGS -Not enough beta testing (hint hint...) - .SH AUTHOR -Andrew McNab +Andrew McNab urlencode is part of GridSite: http://www.gridsite.org/ diff --git a/org.gridsite.core/doc/user.html b/org.gridsite.core/doc/user.html deleted file mode 100644 index ae37cdd..0000000 --- a/org.gridsite.core/doc/user.html +++ /dev/null @@ -1,302 +0,0 @@ -GridSite User Guide - -

GridSite User Guide

- -

If you are setting up a GridSite-based website you may wish to use this -file as the basis of your end-user documentation. If so, copy all of the -files from the GridSite doc directory (probably -/usr/share/doc/gridsite-VERSION/) -to somewhere on your website like -/gridsite-doc/ and add GridSiteHelpURI /gridsite-doc/user.html -to the virtual server configuration in -httpd.conf - you should also look through the rest of the HTML source since -there are some comments you may find helpful. - -

-This Guide is intended for people using GridSite websites with conventional -web browsers, especially people with write access to areas of the site. - There is a separate -Administration Guide - with additional information for people managing access control and group -membership. This Guide assumes you are familiar with basic Web and HTML -concepts. Towards the end we discuss how to access servers with command -line tools like curl and htcp. - -

Reading from HTTP and HTTPS servers

- -

-GridSite servers are usually accessible both via HTTP and via HTTPS. You can -always tell which version you are using by looking at whether the URL in your -browser's location window starts with "http://" or -"https://" HTTPS means that the connection to the server is -encrypted, that you can verify you're talking to the real server and not an -imposter, and gives you the option to authenticate to the site and perhaps -gain write access. - -

- Simple browsing of the website via HTTP or HTTPS is reasonably - self-explanatory. If configured, additional links may appear in the footer - of each webpage with links to this help, - - and to switch between HTTP and HTTPS versions of the page. Pages may also - have a link to the page History, - - showing the dates of changes to that page and names of its authors. - -

- When looking at HTTPS pages, you may find your browser reports it cannot - verify the server's certificate since it does not recognise the - Certification Authority (CA) it uses. You should attempt to load the CA's - root certificate into your browser to stop these warnings. (This means your - browser will be able to identify any servers using fake certificates which - you shouldn't trust.) How you obtain the CA Root Certificate from a - trust-worthy source depends on the CA. For example, the UK e-Science CA - lets you download it from their - website. - - -

Authenticating

- -

- To go beyond reading pages you need to obtain a user certificate and load it - into your web browser. How you do this again depends on the Certification - Authority you have access to (for most Grid projects, CAs are organised - on a national basis.) To use the UK e-Science CA example again, - from their website has links to - the procedure for applying for a certificate from within a web browser. - - -

-A user certificate usually has a version of your name and affiliation as its -Distinguished Name (DN) - for example, -"/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab" - -

-Once you've obtained a user certificate in your name from your CA, you need -to make sure it is loaded into the browser you normally use to browse the -web. How you do this is different for different browsers and to some extent -for different CAs (but if you applied -for the CA through your browser, you may already have it there.) - -

-Browsers want the certificate and private key in the PKCS#12 format, which -is normally a single file with the extension ".p12". -Many programs which are based on OpenSSL, such as Globus and curl, prefer -the PEM (".pem") format for certificates, with separate -certificate and key files ("usercert.pem" and -"userkey.pem", for example.) If you only have the files in .pem -format and have access to openssl, you -can use its command line tools to convert PEM to PKCS#12: -

-openssl pkcs12 -in usercert.pem -inkey userkey.pem -export -out certkey.p12
-
- -

-Be very careful not to accidentally overwrite .pem or .p12 files when -doing this kind of thing! In particular, if you lose your private key, you -cannot retrieve it from your CA. - -

- Once your user certificate is loaded, you should be able to see your - certificate name appear when you look at an HTTPS GridSite page which has - the page footers enabled - for example, the "Switch to HTTP" link - present. If GridSite understands your user certificate, it displays a - "You are ..." line in the footer. (However, the Apache webserver - must also be set up with your CAs root certificate for this to work. The - GridPP HTTPS home page is set up - to recognise a good range of European and North American Grid CAs.) - - -

Authorization

- -

- Once users can prove their identity to the web server, it then becomes - possible to give them appropriate rights depending on that identity. - GridSite allows site administrators to specify these rights for individuals - and groups using -GACL - access control files. (The -Administration Guide - explains how to manage these files.) GACL defines who can - read files, who can list directories, - who can write or create files and who can modify the GACL policy files. To - get increased access to an area of a site, you need to contact the - administrator for that area and give the DN of your certificate (it's not - necessary to send any certificate files.) - -

Managing Directories and Files

- -

-If you have list permission for the directory containing a page, you should -see an extra link "Manage Directory" in the page's set of footer -links, which allows you to browse the directory even if the normal -index.html is present. If page histories are available, this listing view -also has links to them. - -

-The real power of GridSite becomes available if you have write access to a -directory. In that case, the "Manage Directory" page has -additional links to Delete or Rename pages and other files, and to Edit HTML -and plain text files. An Edit link also appears in the footer links of HTML -pages. - -

-If you use the Edit function, you are presented with an HTML form containing -the current filename and the full HTML or plain text of the page for you to -edit. This allows you to maintain the content of the site "in -place" and to see the result of your changes immediately, in context. - -

-If you modify the filename in the form before saving, GridSite will make a -new file with that name, and the old file will still be present, unmodified. -(However, you cannot use this feature for creating a file in a different -directory.) -As you make changes, the history of the changes and your certificate DN are -recorded, and available in the history page for that file. - -

- For people with write access, the "Manage Directory" page also has - options to upload a file from the computer your browser is running on, and to - create files and directories. If it's enabled, you can also view the - contents of WinZIP / PKZIP / .zip files, and unpack their contents into the - current directory. (This feature is very useful if you have several files - to upload at one time.) - - -

HTML Formatting in GridSite

- -

-As well as providing access control and file management, GridSite provides -some simple formatting of HTML pages by adding standard headers and footers. -(If this isn't sufficient, GridSite will happily coexist with HTML -preprocessor languages like SSI, PHP and JSP.) - -

- If HTML formatting is enabled - for the current directory, GridSite looks for the files gridsitehead.txt and - gridsitefoot.txt in that directory, or goes up through the parent - directories until they are found. - - -

-The <body> and </body> tags from the HTML file are replaced with -the contents of the gridsitehead.txt and gridsitefoot.txt files, which -should normally be chunks of HTML including a replacement <body> -or </body> tag. If either tag is absent from the original page, then -the header or footer is just added rather than being inserted in place of -the tag. (One consequence of this absence is that HTML header tags like -<title> can end up after a <body> tag, and can get ignored by -browsers - so always include <body> ... </body> in your pages.) - -

-This simple system is suprisingly flexible, and allows a variety of top and -bottom, or sidebar navigation layouts of pages. Since the <body ...> -tag is under full control of the author of the gridsitehead.txt file, -backgrounds, colour schemes and style sheets can easily be specified. - -

-For example: - -

- - - - - - - - - -
SourceHTML
page.html<title>PAGE TITLE</title>
page.html
(replaced)
<body>
gridsitehead.txt<body text=blue>
- Heading text
- <table border=1>
<tr>
<td>Standard<br>
- sidebar</td>
<td>
page.html<p>
Page content...
page.html
(replaced)
</body>
gridsitefoot.txt</td>
</tr>
- </table>
Footer text
</body>
- -

-produces pages with a layout like: - -

- - - - -
Heading text
Standard
sidebar
Page content...
Footer text
- -

Command line use

- -

-GridSite adds support for the HTTP PUT and DELETE methods, and this makes it -easy to create or delete files from within programs and commands without -using a web browser and HTML forms. It is straightforward, although slightly -awkward, to use a standard HTTPS-aware client like -curl to upload files, but GridSite -provides htcp as a more convenient client program, which is easier to use -with GSI Proxies and X.509 user certificates, and has a syntax closer to the -familiar scp command. - -

-The following examples assume the GridSite server has GSI support and use a -GSI proxy as the client certificate. For non-GSI use, just skip the -grid-proxy-init stage, and replace the proxy -filename with $HOME/.globus/usercert.pem and $HOME/.globus/userkey.pem (or -wherever your PEM format certificate and key are stored.) - -

-First generate a GSI proxy with grid-proxy-init. This will create a proxy file -in /tmp/x509up_uXXXXX where XXXXX is your Unix UID (also given by id --u.) The GSI proxy contains a -temporary private key and certificate signed by your long-term user -certificate. - -

-You should make sure you have a copy of the CA root certificates of the CA's -used by the servers you wish to talk to. These are usually installed in -/etc/grid-security/certificates as files like 01621954.0, and RPMs and tar -files for many common European and North American CAs are available from - -https://datagrid.in2p3.fr/distribution/datagrid/security/ - -

-To upload a file with curl: -

-curl --cert /tmp/x509up_u`id -n` --key /tmp/x509up_u`id -n` \
-     --capath /etc/grid-security/certificates \
-     --upload-file /tmp/new.file.txt https://server/new.file.txt
-
- -

-The equivalent htcp command is: -

-htcp /tmp/new.file.txt https://server/new.file.txt
-
-since htcp looks for the GSI proxy and CA certificates automatically. htcp -can also be used to copy remote files to the local machine by reversing the -arguments. For more details, see the -htcp(1) man page. - -

-htcp also has options for deleting files, and doing short or long listings, -and these can also be accessed using the htrm, htls and htll commands (which -are normally symbolic links to htcp.) - -

-Directory indexes are based on parsing the index returned by the web server -and by using the HTTP HEAD method to obtain the file size and modification -times. - -

-All of the ht** commands can accept multiple source file arguments, and this -allows you to copy multiple files to or from the server. Shell wildcard -expansion on the local machine is especially useful: -

-htcp /tmp/new.*.txt https://server/
-
- - diff --git a/org.gridsite.core/interface/gridsite.h b/org.gridsite.core/interface/gridsite.h index e252019..d9473a8 100644 --- a/org.gridsite.core/interface/gridsite.h +++ b/org.gridsite.core/interface/gridsite.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2002-3, Andrew McNab, University of Manchester + Copyright (c) 2002-5, Andrew McNab, University of Manchester All rights reserved. Redistribution and use in source and binary forms, with or @@ -124,6 +124,39 @@ struct GRSTasn1TagList { char treecoords[GRST_ASN1_MAXCOORDLEN+1]; int length; int tag; } ; +#define GRST_HTTP_PORT 777 +#define GRST_HTTPS_PORT 488 +#define GRST_HTCP_PORT 777 + +#define GRSThtcpNOPop 0 +#define GRSThtcpTSTop 1 + +typedef struct { unsigned char length_msb; + unsigned char length_lsb; + char text[1]; } GRSThtcpCountstr; + +#define GRSThtcpCountstrLen(string) (256*((string)->length_msb) + (string)->length_lsb) + +typedef struct { unsigned char total_length_msb; + unsigned char total_length_lsb; + unsigned char version_msb; + unsigned char version_lsb; + unsigned char data_length_msb; + unsigned char data_length_lsb; + unsigned int response : 4; + unsigned int opcode : 4; + unsigned int rr : 1; + unsigned int f1 : 1; + unsigned int reserved : 6; + unsigned int trans_id; /* must be 4 bytes */ + GRSThtcpCountstr *method; + GRSThtcpCountstr *uri; + GRSThtcpCountstr *version; + GRSThtcpCountstr *req_hdrs; + GRSThtcpCountstr *resp_hdrs; + GRSThtcpCountstr *entity_hdrs; + GRSThtcpCountstr *cache_hdrs; } GRSThtcpMessage; + int GRSTgaclInit(void); /* #define GACLnewCred(x) GRSTgaclCredNew((x)) */ @@ -251,7 +284,7 @@ int GRSTx509VerifyCallback(int, X509_STORE_CTX *); int GRSTx509GetVomsCreds(int *, int, size_t, char *, X509 *, STACK_OF(X509) *, char *); GRSTgaclCred *GRSTx509CompactToCred(char *); -int GRSTx509CompactCreds(int *, int, size_t, char *, STACK_OF(X509) *, char *); +int GRSTx509CompactCreds(int *, int, size_t, char *, STACK_OF(X509) *, char *, X509 *); char *GRSTx509CachedProxyFind(char *, char *, char *); char *GRSTx509FindProxyFileName(void); int GRSTx509MakeProxyCert(char **, FILE *, char *, char *, char *, int); @@ -285,3 +318,9 @@ int GRSTasn1ParseDump(BIO *, unsigned char *, long, struct GRSTasn1TagList taglist[], int, int *); int GRSTasn1GetX509Name(char *, int, char *, char *, struct GRSTasn1TagList taglist[], int); + +int GRSThtcpNOPrequestMake(char **, int *, unsigned int); +int GRSThtcpNOPresponseMake(char **, int *, unsigned int); +int GRSThtcpTSTrequestMake(char **, int *, unsigned int, char *, char *, char *); +int GRSThtcpTSTresponseMake(char **, int *, unsigned int, char *, char *, char *); +int GRSThtcpMessageParse(GRSThtcpMessage *, char *, int); diff --git a/org.gridsite.core/project/version.properties b/org.gridsite.core/project/version.properties index 87511c8..55c7065 100644 --- a/org.gridsite.core/project/version.properties +++ b/org.gridsite.core/project/version.properties @@ -1,2 +1,2 @@ -module.version=1.1.11 +module.version=1.1.14 module.age=1 diff --git a/org.gridsite.core/src/Makefile b/org.gridsite.core/src/Makefile index 3552c0f..a465cf4 100644 --- a/org.gridsite.core/src/Makefile +++ b/org.gridsite.core/src/Makefile @@ -56,7 +56,8 @@ endif # Build # -build: libgridsite.so.$(VERSION) libgridsite.a htcp mod_gridsite.so \ +build: apidoc \ + libgridsite.so.$(VERSION) libgridsite.a htcp mod_gridsite.so \ urlencode findproxyfile real-gridsite-admin.cgi gsexec \ # gridsite-delegation.cgi # htproxyput @@ -64,40 +65,44 @@ build: libgridsite_globus.so.$(VERSION) libgridsite_globus.a # First, normal versions using system OpenSSL rather than Globus OpenSSL -libgridsite.so.$(VERSION): grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o +libgridsite.so.$(VERSION): grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o gcc -shared -Wl,-soname,libgridsite.so.$(MINOR_VERSION) \ - -o libgridsite.so.$(PATCH_VERSION) grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o + -o libgridsite.so.$(PATCH_VERSION) grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o -libgridsite.a: grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o - ar src libgridsite.a grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o +libgridsite.a: grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o + ar src libgridsite.a grst_x509.o grst_gacl.o grst_xacml.o grst_http.o grst_asn1.o grst_htcp.o grst_x509.o: grst_x509.c ../interface/gridsite.h - gcc $(MYCFLAGS) \ + gcc -g $(MYCFLAGS) \ -I/usr/kerberos/include -c grst_x509.c grst_gacl.o: grst_gacl.c ../interface/gridsite.h - gcc $(MYCFLAGS) \ + gcc -g $(MYCFLAGS) \ -I/usr/kerberos/include `xml2-config --cflags` -c grst_gacl.c grst_xacml.o: grst_xacml.c ../interface/gridsite.h - gcc $(MYCFLAGS) \ + gcc -g $(MYCFLAGS) \ -I/usr/kerberos/include `xml2-config --cflags` -c grst_xacml.c grst_http.o: grst_http.c ../interface/gridsite.h - gcc $(MYCFLAGS) \ + gcc -g $(MYCFLAGS) \ -I/usr/kerberos/include -c grst_http.c grst_asn1.o: grst_asn1.c ../interface/gridsite.h - gcc $(MYCFLAGS) \ + gcc -g $(MYCFLAGS) \ -I/usr/kerberos/include -c grst_asn1.c +grst_htcp.o: grst_htcp.c ../interface/gridsite.h + gcc -g $(MYCFLAGS) \ + -I/usr/kerberos/include -c grst_htcp.c + # Then build versions using Globus OpenSSL if configured ifdef OPENSSL_GLOBUS_LIBS libgridsite_globus.so.$(VERSION): \ grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o \ - grst_asn1_globus.o grst_xacml_globus.o + grst_asn1_globus.o grst_xacml_globus.o grst_htcp_globus.o gcc -shared -Wl,-soname,libgridsite_globus.so.$(MINOR_VERSION) \ -o libgridsite_globus.so.$(PATCH_VERSION) \ grst_x509_globus.o grst_gacl_globus.o grst_xacml_globus.o grst_http_globus.o grst_asn1_globus.o @@ -107,30 +112,35 @@ libgridsite_globus.a: grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o g grst_x509_globus.o grst_gacl_globus.o grst_http_globus.o grst_asn1_globus.o grst_x509_globus.o: grst_x509.c ../interface/gridsite.h - gcc $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ -I/usr/kerberos/include -c grst_x509.c \ -o grst_x509_globus.o grst_gacl_globus.o: grst_gacl.c ../interface/gridsite.h - gcc $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ -I/usr/kerberos/include `xml2-config --cflags` -c grst_gacl.c \ -o grst_gacl_globus.o grst_xacml_globus.o: grst_xacml.c ../interface/gridsite.h - gcc $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ -I/usr/kerberos/include `xml2-config --cflags` -c grst_xacml.c \ -o grst_xacml_globus.o grst_http_globus.o: grst_http.c ../interface/gridsite.h - gcc $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ -I/usr/kerberos/include -c grst_http.c \ -o grst_http_globus.o grst_asn1_globus.o: grst_asn1.c ../interface/gridsite.h - gcc $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ -I/usr/kerberos/include -c grst_asn1.c \ -o grst_asn1_globus.o +grst_htcp_globus.o: grst_htcp.c ../interface/gridsite.h + gcc -g $(MYCFLAGS) $(OPENSSL_GLOBUS_FLAGS) \ + -I/usr/kerberos/include -c grst_htcp.c \ + -o grst_htcp_globus.o + else libgridsite_globus.so.$(VERSION): libgridsite.so.$(VERSION) @@ -142,21 +152,22 @@ libgridsite_globus.a: libgridsite.a endif gsexec: gsexec.c gsexec.h - gcc -DVERSION=\"$(PATCH_VERSION)\" -I/usr/include/httpd \ - -I/usr/include/apr-0 \ + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ -o gsexec gsexec.c urlencode: urlencode.c libgridsite.a - gcc -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ -o urlencode urlencode.c -L. \ - -I/usr/kerberos/include -lgridsite + -I/usr/kerberos/include -lgridsite -htcp: htcp.c - gcc -DVERSION=\"$(PATCH_VERSION)\" -I. -o htcp htcp.c \ - `curl-config --cflags` `curl-config --libs` +htcp: htcp.c libgridsite.a + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) \ + -o htcp htcp.c -L. \ + -I/usr/kerberos/include \ + `curl-config --cflags` `curl-config --libs` -lgridsite mod_gridsite.so: mod_gridsite.c mod_ssl-private.h libgridsite.a - gcc $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \ + gcc -g $(MYCFLAGS) -shared -Wl,-soname=gridsite_module \ -I/usr/kerberos/include \ -I/usr/include/libxml2 \ -DVERSION=\"$(VERSION)\" -o mod_gridsite.so \ @@ -164,7 +175,7 @@ mod_gridsite.so: mod_gridsite.c mod_ssl-private.h libgridsite.a real-gridsite-admin.cgi: grst_admin_main.c grst_admin_gacl.c \ grst_admin_file.c grst_admin.h - gcc $(MYCFLAGS) $(MYLDFLAGS) -o real-gridsite-admin.cgi \ + gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o real-gridsite-admin.cgi \ grst_admin_main.c \ grst_admin_gacl.c \ grst_admin_file.c \ @@ -172,28 +183,32 @@ real-gridsite-admin.cgi: grst_admin_main.c grst_admin_gacl.c \ -DVERSION=\"$(VERSION)\" -lgridsite -lssl -lcrypto -lxml2 -lz -lm findproxyfile: findproxyfile.c libgridsite.a - gcc -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \ + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \ -o findproxyfile findproxyfile.c -L. \ -I/usr/kerberos/include -lgridsite \ -lssl -lcrypto -lxml2 -lz -lm showx509exts: showx509exts.c libgridsite.a - gcc -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \ + gcc -g -DVERSION=\"$(PATCH_VERSION)\" $(MYCFLAGS) $(MYLDFLAGS) \ -o showx509exts showx509exts.c -L. \ -I/usr/kerberos/include \ -lgridsite \ - -lssl -lcrypto + -lssl -lcrypto -lxml2 -lz -lm apidoc: doxygen Doxyfile + mkdir -p ../doc/doxygen + cp -f doxygen/*.html doxygen/*.css doxygen/*.png ../doc/doxygen + cd ../doc ; for i in *.1 *.8 ; do ../src/roffit < $$i \ + > $$i.html ; done gaclexample: gaclexample.c libgridsite.a - gcc -o gaclexample gaclexample.c -I. -L. \ + gcc -g -o gaclexample gaclexample.c -I. -L. \ -I/usr/kerberos/include -lgridsite \ -lssl -lcrypto -lxml2 -lz -lm xacmlexample: xacmlexample.c libgridsite.a - gcc -o xacmlexample xacmlexample.c -I. -L. \ + gcc -g -o xacmlexample xacmlexample.c -I. -L. \ -I/usr/kerberos/include -lgridsite \ -lssl -lcrypto -lxml2 -lz -lm @@ -212,12 +227,12 @@ delegation.wsdl: delegation.h $(GSOAPDIR)/bin/soapcpp2 -c delegation.h libstdsoap2.a: $(GSOAPDIR)/stdsoap2.c - gcc -c -DWITH_OPENSSL $(GSOAPDIR)/stdsoap2.c + gcc -g -c -DWITH_OPENSSL $(GSOAPDIR)/stdsoap2.c ar src libstdsoap2.a stdsoap2.o gridsite-delegation.cgi: grst-delegation.c delegation.h delegation.wsdl \ soapC.c soapServer.c - gcc $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-delegation.cgi \ + gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o gridsite-delegation.cgi \ grst-delegation.c \ -I/usr/kerberos/include -I$(GSOAPDIR)/include \ -DVERSION=\"$(VERSION)\" -L$(GSOAPDIR)/lib \ @@ -226,7 +241,7 @@ gridsite-delegation.cgi: grst-delegation.c delegation.h delegation.wsdl \ htproxyput: htproxyput.c delegation.h delegation.wsdl \ soapC.c soapServer.c - gcc $(MYCFLAGS) $(MYLDFLAGS) -o htproxyput \ + gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o htproxyput \ htproxyput.c \ -I/usr/kerberos/include \ -g -DVERSION=\"$(VERSION)\" \ @@ -236,7 +251,7 @@ htproxyput: htproxyput.c delegation.h delegation.wsdl \ proxyput-example: proxyput-example.c delegation.h delegation.wsdl \ soapC.c soapServer.c - gcc $(MYCFLAGS) $(MYLDFLAGS) -o proxyput-example \ + gcc -g $(MYCFLAGS) $(MYLDFLAGS) -o proxyput-example \ proxyput-example.c \ -I/usr/kerberos/include \ -g -DVERSION=\"$(VERSION)\" \ @@ -280,24 +295,22 @@ install: apidoc $(prefix)/lib/libgridsite_globus.so.$(MAJOR_VERSION) ln -sf libgridsite_globus.so.$(PATCH_VERSION) \ $(prefix)/lib/libgridsite_globus.so.$(MINOR_VERSION) - cp -f doxygen/index.html \ - $(prefix)/share/doc/gridsite-$(PATCH_VERSION)/doxygen-index.html - cp -f doxygen/* $(prefix)/share/doc/gridsite-$(PATCH_VERSION) cp -f ../CHANGES ../README ../INSTALL ../LICENSE ../VERSION \ $(prefix)/share/doc/gridsite-$(PATCH_VERSION) cp -f ../doc/*.html ../doc/*.conf ../doc/*.1 ../doc/*.8 ../doc/*.sh \ - $(prefix)/share/doc/gridsite-$(VERSION) + ../doc/*.wsdl $(prefix)/share/doc/gridsite-$(VERSION) cp -f ../doc/*.1 $(prefix)/share/man/man1 cp -f ../doc/*.8 $(prefix)/share/man/man8 gzip -f $(prefix)/share/man/man1/*.1 gzip -f $(prefix)/share/man/man8/*.8 - cd ../doc ; for i in *.1 *.8 ; do ../src/roffit < $$i \ - > $(prefix)/share/doc/gridsite-$(VERSION)/$$i.html ; done cp -f htcp $(prefix)/bin ln -sf htcp $(prefix)/bin/htls ln -sf htcp $(prefix)/bin/htll ln -sf htcp $(prefix)/bin/htrm ln -sf htcp $(prefix)/bin/htmkdir + ln -sf htcp $(prefix)/bin/htmv + ln -sf htcp $(prefix)/bin/htping + ln -sf htcp $(prefix)/bin/htfind cp -f gsexec $(prefix)/sbin cp -f mod_gridsite.so $(prefix)/lib/httpd/modules @@ -320,7 +333,7 @@ dist: Doxyfile doxygen.css doxyheader.html \ ../gridsite-$(PATCH_VERSION)/src cp -f ../doc/*.html ../doc/*.1 ../doc/*.8 ../doc/*.conf ../doc/*.sh \ - ../gridsite-$(PATCH_VERSION)/doc + ../doc/*.wsdl ../gridsite-$(PATCH_VERSION)/doc cp -f ../interface/*.h \ ../gridsite-$(PATCH_VERSION)/interface cd .. ; tar zcvf gridsite-$(PATCH_VERSION).src.tar.gz \ @@ -334,12 +347,16 @@ htcp-bin: htcp ../htcp-bin-$(PATCH_VERSION)/man/man1 cp -f ../doc/README.htcp-bin ../htcp-bin-$(PATCH_VERSION) cp -f htcp ../htcp-bin-$(PATCH_VERSION)/bin - cp -f ../doc/htcp.1 ../doc/htrm.1 ../doc/htls.1 ../doc/htll.1 \ - ../doc/htmkdir.1 ../htcp-bin-$(PATCH_VERSION)/man/man1 + cp -f ../doc/htcp.1 ../doc/htrm.1 ../doc/htls.1 ../doc/htmkdir.1 \ + ../doc/htll.1 ../doc/htmv.1 ../doc/htping.1 ../doc/htfind.1 \ + ../htcp-bin-$(PATCH_VERSION)/man/man1 ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htls ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htll ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htrm ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htmkdir + ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htmv + ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htping + ln -sf htcp ../htcp-bin-$(PATCH_VERSION)/bin/htfind cd ../htcp-bin-$(VERSION) ; tar zcvf ../htcp-$(VERSION).bin.tar.gz . rm -Rf ../htcp-bin-$(PATCH_VERSION) diff --git a/org.gridsite.core/src/gridsite.spec b/org.gridsite.core/src/gridsite.spec index f55c349..0819eb0 100644 --- a/org.gridsite.core/src/gridsite.spec +++ b/org.gridsite.core/src/gridsite.spec @@ -8,7 +8,7 @@ Source: %{name}-%{version}.src.tar.gz Prefix: %(echo ${MYPREFIX:-/usr}) URL: http://www.gridsite.org/ Vendor: GridPP -#Requires: libxml2,curl-ssl,mod_ssl +Requires: libxml2 #Buildrequires: libxml2-devel,curl-ssl-devel,httpd-devel Packager: Andrew McNab @@ -23,7 +23,7 @@ http://www.gridsite.org/ for details. %package -n htcp Group: Applications/Internet Summary: HTTP(S) read/write client -#Requires: curl-ssl +Requires: curl %description -n htcp htcp is a client to fetch files or directory listings from remote @@ -34,7 +34,6 @@ HTTP/HTTPS rather than ssh as its transfer protocol. %package gsexec Group: Applications/Internet Summary: gsexec binary for the Apache HTTP server -#Requires: curl-ssl %description gsexec This package includes the /usr/sbin/gsexec binary which can be installed @@ -64,7 +63,7 @@ OPENSSL_LIBS=$OPENSSL_LIBS FLAVOR_EXT=$FLAVOR_EXT ln -sf %(echo ${MYPREFIX:-/usr})/share/doc/gridsite-%{version} \ %(echo ${MYPREFIX:-/usr})/share/doc/gridsite -%postun +#%postun rm -f %(echo ${MYPREFIX:-/usr})/share/doc/gridsite %files @@ -77,6 +76,7 @@ rm -f %(echo ${MYPREFIX:-/usr})/share/doc/gridsite %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/doc/gridsite-%{version} %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/urlencode.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/findproxyfile.1.gz +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man8/mod_gridsite.8.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/include/gridsite.h %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/include/gridsite-gacl.h %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/lib/libgridsite.a @@ -90,11 +90,17 @@ rm -f %(echo ${MYPREFIX:-/usr})/share/doc/gridsite %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htll %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htrm %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmkdir +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htmv +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htping +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/bin/htfind %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htcp.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htrm.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htls.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htll.1.gz %attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htmkdir.1.gz +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htmv.1.gz +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htping.1.gz +%attr(-, root, root) %(echo ${MYPREFIX:-/usr})/share/man/man1/htfind.1.gz %files gsexec %attr(4510, root, apache) %(echo ${MYPREFIX:-/usr})/sbin/gsexec diff --git a/org.gridsite.core/src/grst_asn1.c b/org.gridsite.core/src/grst_asn1.c index e47c143..bc92a87 100644 --- a/org.gridsite.core/src/grst_asn1.c +++ b/org.gridsite.core/src/grst_asn1.c @@ -469,14 +469,14 @@ int GRSTasn1GetX509Name(char *x509name, int maxlength, char *coords, ASN1_OBJECT *obj = NULL; unsigned char coordstmp[81], *q; const unsigned char *shortname; - + for (i=1; ; ++i) { - sprintf(coordstmp, coords, i, 1); + snprintf(coordstmp, sizeof(coordstmp), coords, i, 1); iobj = GRSTasn1SearchTaglist(taglist, lasttag, coordstmp); if (iobj < 0) break; - sprintf(coordstmp, coords, i, 2); + snprintf(coordstmp, sizeof(coordstmp), coords, i, 2); istr = GRSTasn1SearchTaglist(taglist, lasttag, coordstmp); if (istr < 0) break; @@ -502,5 +502,5 @@ int GRSTasn1GetX509Name(char *x509name, int maxlength, char *coords, x509name[len] = '\0'; - return GRST_RET_OK; + return (x509name[0] != '\0') ? GRST_RET_OK : GRST_RET_FAILED; } diff --git a/org.gridsite.core/src/grst_gacl.c b/org.gridsite.core/src/grst_gacl.c index 1df2f02..336c853 100644 --- a/org.gridsite.core/src/grst_gacl.c +++ b/org.gridsite.core/src/grst_gacl.c @@ -646,17 +646,25 @@ GRSTgaclAcl *GRSTgaclAclLoadFile(char *filename) if (doc == NULL) return NULL; cur = xmlDocGetRootElement(doc); - if (cur == NULL) return NULL; + if (cur == NULL) + { + xmlFreeDoc(doc); + return NULL; + } - if (!xmlStrcmp(cur->name, (const xmlChar *) "Policy")) { acl=GRSTxacmlAclParse(doc, cur, acl);} - else if (!xmlStrcmp(cur->name, (const xmlChar *) "gacl")) {acl=GRSTgaclAclParse(doc, cur, acl);} + if (!xmlStrcmp(cur->name, (const xmlChar *) "Policy")) + { + acl=GRSTxacmlAclParse(doc, cur, acl); + } + else if (!xmlStrcmp(cur->name, (const xmlChar *) "gacl")) + { + acl=GRSTgaclAclParse(doc, cur, acl); + } else /* ACL format not recognised */ { - free(doc); - free(cur); + xmlFreeDoc(doc); return NULL; } - xmlFreeDoc(doc); return acl; diff --git a/org.gridsite.core/src/grst_htcp.c b/org.gridsite.core/src/grst_htcp.c new file mode 100644 index 0000000..ec9672a --- /dev/null +++ b/org.gridsite.core/src/grst_htcp.c @@ -0,0 +1,311 @@ +/* + Copyright (c) 2002-5, Andrew McNab, University of Manchester + All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, are permitted provided that the following + conditions are met: + + o Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. + o Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS + BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef VERSION +#define VERSION "x.x.x" +#endif + +#define _GNU_SOURCE +#include + +#include +#include +#include +#include +#include + +#include "gridsite.h" + +int GRSThtcpNOPrequestMake(char **request, int *request_length, + unsigned int trans_id) +/* + Make a complete HTCP NOP request and return a pointer to malloc'd + memory pointing to it. +*/ +{ + *request_length = + asprintf(request,"%c%c" /* place holder for total length */ + "%c%c" /* HTCP version 0.0 */ + "%c%c" /* DATA length place holder */ + "%c%c" /* OPCODE,RESPONSE,RESERVED,F1,RR */ + "%c%c%c%c" /* TRANS-ID placeholder */ + "%c%c", /* AUTH (LENGTH=2 means no AUTH) */ + 0, 0, + 0, 0, + 0, 0, + GRSThtcpNOPop * 16, 2, + 0, 0, 0, 0, + 0, 2); + + if (*request_length < 0) return GRST_RET_FAILED; + + (*request)[0] = *request_length / 256; + (*request)[1] = *request_length % 256; + + (*request)[4] = (*request_length - 6) / 256; + (*request)[5] = (*request_length - 6) % 256; + + memcpy(&((*request)[8]), &trans_id, 4); + + return GRST_RET_OK; +} + +int GRSThtcpNOPresponseMake(char **message, int *message_length, + unsigned int trans_id) +/* + Make a complete HTCP NOP response for a found file and return a pointer + to malloc'd memory pointing to it. +*/ +{ + *message_length = + asprintf(message, + "%c%c" /* place holder for total length */ + "%c%c" /* HTCP version 0.0 */ + "%c%c" /* DATA length place holder */ + "%c%c" /* OPCODE,RESPONSE,RESERVED,F1,RR */ + "%c%c%c%c" /* TRANS-ID place holder */ + "%c%c", /* AUTH (LENGTH=2 means no AUTH) */ + 0, 0, + 0, 0, + 0, 0, + GRSThtcpNOPop * 16, 1, /* RR=1, MO=0, RESPONSE=0 (ie found) */ + 0, 0, 0, 0, + 0, 2); + + if (*message_length < 0) return GRST_RET_FAILED; + + (*message)[0] = *message_length / 256; + (*message)[1] = *message_length % 256; + + (*message)[4] = (*message_length - 6) / 256; + (*message)[5] = (*message_length - 6) % 256; + + memcpy(&((*message)[8]), &trans_id, 4); + + return GRST_RET_OK; +} + +int GRSThtcpTSTrequestMake(char **request, int *request_length, + unsigned int trans_id, + char *method, char *uri, char *req_hdrs) +/* + Make a complete HTCP TST request and return a pointer to malloc'd + memory pointing to it. +*/ +{ + if ((method == NULL) || (uri == NULL) || (req_hdrs == NULL)) + return GRST_RET_FAILED; + + *request_length = + asprintf(request,"%c%c" /* place holder for total length */ + "%c%c" /* HTCP version 0.0 */ + "%c%c" /* DATA length place holder */ + "%c%c" /* OPCODE,RESPONSE,RESERVED,F1,RR */ + "%c%c%c%c" /* TRANS-ID placeholder */ + "%c%c%s" /* OP-DATA: METHOD */ + "%c%c%s" /* OP-DATA: URI */ + "%c%c%s" /* OP-DATA: VERSION */ + "%c%c%s" /* OP-DATA: REQ-HDRS */ + "%c%c", /* AUTH (LENGTH=2 means no AUTH) */ + 0, 0, + 0, 0, + 0, 0, + GRSThtcpTSTop * 16, 2, + 0, 0, 0, 0, + strlen(method) / 256, strlen(method) % 256, method, + strlen(uri) / 256, strlen(uri) % 256, uri, + 0, 8, "HTTP/1.1", + strlen(req_hdrs)/256, strlen(req_hdrs) % 256, req_hdrs, + 0, 2); + + if (*request_length < 0) return GRST_RET_FAILED; + + (*request)[0] = *request_length / 256; + (*request)[1] = *request_length % 256; + + (*request)[4] = (*request_length - 6) / 256; + (*request)[5] = (*request_length - 6) % 256; + + memcpy(&((*request)[8]), &trans_id, 4); + + return GRST_RET_OK; +} + +int GRSThtcpTSTresponseMake(char **message, int *message_length, + unsigned int trans_id, + char *resp_hdrs, char *entity_hdrs, + char *cache_hdrs) +/* + Make a complete HTCP TST response for a found file and return a pointer + to malloc'd memory pointing to it. +*/ +{ + if ((resp_hdrs != NULL) && (entity_hdrs != NULL) && (cache_hdrs != NULL)) + /* found file response */ + *message_length = + asprintf(message, + "%c%c" /* place holder for total length */ + "%c%c" /* HTCP version 0.0 */ + "%c%c" /* DATA length place holder */ + "%c%c" /* OPCODE,RESPONSE,RESERVED,F1,RR */ + "%c%c%c%c" /* TRANS-ID place holder */ + "%c%c%s" /* OP-DATA: RESP-HDRS */ + "%c%c%s" /* OP-DATA: ENTITY-HDRS */ + "%c%c%s" /* OP-DATA: CACHE-HDRS */ + "%c%c", /* AUTH (LENGTH=2 means no AUTH) */ + 0, 0, + 0, 0, + 0, 0, + GRSThtcpTSTop * 16, 1, /* RR=1, MO=0, RESPONSE=0 (ie found) */ + 0, 0, 0, 0, + strlen(resp_hdrs) / 256, strlen(resp_hdrs) % 256, resp_hdrs, + strlen(entity_hdrs) / 256, strlen(entity_hdrs) % 256, entity_hdrs, + strlen(cache_hdrs) / 256, strlen(cache_hdrs) % 256, cache_hdrs, + 0, 2); + else if (cache_hdrs != NULL) + /* not found file response, just cache_hdrs given */ + *message_length = + asprintf(message, + "%c%c" /* place holder for total length */ + "%c%c" /* HTCP version 0.0 */ + "%c%c" /* DATA length place holder */ + "%c%c" /* OPCODE,RESPONSE,RESERVED,F1,RR */ + "%c%c%c%c" /* TRANS-ID */ + "%c%c%s" /* OP-DATA: CACHE-HDRS */ + "%c%c", /* AUTH (LENGTH=2 means no AUTH) */ + 0, 0, + 0, 0, + 0, 0, + GRSThtcpTSTop * 16 + 1, 1, /* RR=1, MO=0, RESPONSE=1 (missing) */ + 0, 0, 0, 0, + strlen(cache_hdrs) / 256, strlen(cache_hdrs) % 256, cache_hdrs, + 0, 2); + else return GRST_RET_FAILED; + + if (*message_length < 0) return GRST_RET_FAILED; + + (*message)[0] = *message_length / 256; + (*message)[1] = *message_length % 256; + + (*message)[4] = (*message_length - 6) / 256; + (*message)[5] = (*message_length - 6) % 256; + + memcpy(&((*message)[8]), &trans_id, 4); + + return GRST_RET_OK; +} + +int GRSThtcpMessageParse(GRSThtcpMessage *parsed, char *raw, int length) +{ + GRSThtcpCountstr *s; + + bzero(parsed, sizeof(GRSThtcpMessage)); + + if (length < (void *) &(parsed->method) + - (void *) &(parsed->total_length_msb) + 2) + return GRST_RET_FAILED; + + memcpy(parsed, raw, (void *) &(parsed->method) + - (void *) &(parsed->total_length_msb)); + + if (parsed->opcode == GRSThtcpNOPop) return GRST_RET_OK; + + if ((parsed->opcode == GRSThtcpTSTop) && (parsed->rr == 0)) + { + /* a TST request */ + + /* point to start of data/auth in raw */ + s = (GRSThtcpCountstr *) &(((GRSThtcpMessage *) raw)->method); + + /* METHOD string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->method = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + /* URI string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->uri = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + /* VERSION string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->version = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + /* REQ-HDRS string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->req_hdrs = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + return GRST_RET_OK; + } + + if ((parsed->opcode == GRSThtcpTSTop) && (parsed->rr == 1)) + { + /* a TST response */ + + /* point to start of data/auth in raw */ + s = (GRSThtcpCountstr *) &(((GRSThtcpMessage *) raw)->method); + + /* RESP-HDRS string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->resp_hdrs = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + /* ENTITY-HDRS string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->entity_hdrs = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + /* CACHE-HDRS string */ + + if ((void *) s + 2 + GRSThtcpCountstrLen(s) > (void *) raw + length) + return GRST_RET_FAILED; + parsed->cache_hdrs = s; + s = (GRSThtcpCountstr *) ((void *) s + 2 + GRSThtcpCountstrLen(s)); + + return GRST_RET_OK; + } + + return GRST_RET_FAILED; +} diff --git a/org.gridsite.core/src/grst_http.c b/org.gridsite.core/src/grst_http.c index c7b375e..c933ca0 100644 --- a/org.gridsite.core/src/grst_http.c +++ b/org.gridsite.core/src/grst_http.c @@ -405,3 +405,39 @@ char *GRSThttpUrlMildencode(char *in) *q = '\0'; return out; } + +/// Return a one-time passcode string, for use with GridHTTP +/** + * Returns + * + * String is timestamp+SHA1_HASH(timestamp+":"+method+":"+URL) + * Timestamps and hashes are in lowercase hexadecimal. Timestamps are + * seconds since 00:00:00 on January 1, 1970 UTC. + */ + +/* +char *GRSThttpMakeOneTimePasscode(time_t timestamp, char *method, char *url) +{ + int len, i; + char *stringtohash, hashedstring[EVP_MAX_MD_SIZE], *returnstring; + const EVP_MD *m; + EVP_MD_CTX ctx; + + m = EVP_sha1(); + if (m == NULL) return NULL; + + asprintf(&stringtohash, "%08x:%s:%s", timestamp, method, url); + + EVP_DigestInit(&ctx, m); + EVP_DigestUpdate(&ctx, stringtohash, strlen(stringtohash)); + EVP_DigestFinal(&ctx, hashedstring, &len); + + returnstring = malloc(9 + len * 2); + + sprintf(returnstring, "%08x", timestamp); + + for (i=0; + + return returnstring; +} +*/ diff --git a/org.gridsite.core/src/grst_x509.c b/org.gridsite.core/src/grst_x509.c index ccf6100..4bbdc04 100644 --- a/org.gridsite.core/src/grst_x509.c +++ b/org.gridsite.core/src/grst_x509.c @@ -374,13 +374,14 @@ static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, unsigned char *asn1string, struct GRSTasn1TagList taglist[], int lasttag, - char *vomsdir) + char *vomsdir, int acnumber) { -#define GRST_ASN1_COORDS_VOMS_DN "-1-1-1-1-3-1-1-1-%d-1-%d" -#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-1-1" -#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-1-3" +#define GRST_ASN1_COORDS_VOMS_DN "-1-1-%d-1-3-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_VOMS_INFO "-1-1-%d-1" +#define GRST_ASN1_COORDS_VOMS_SIG "-1-1-%d-3" int ret, isig, iinfo; - char *certpath, acvomsdn[200]; + char *certpath, acvomsdn[200], dn_coords[200], + info_coords[200], sig_coords[200]; unsigned char *q; DIR *vomsDIR; struct dirent *vomsdirent; @@ -391,13 +392,20 @@ static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, time_t voms_service_time1, voms_service_time2; if ((vomsdir == NULL) || (vomsdir[0] == '\0')) return GRST_RET_FAILED; + + snprintf(dn_coords, sizeof(dn_coords), + GRST_ASN1_COORDS_VOMS_DN, acnumber); - if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), - GRST_ASN1_COORDS_VOMS_DN, + if (GRSTasn1GetX509Name(acvomsdn, sizeof(acvomsdn), dn_coords, asn1string, taglist, lasttag) != GRST_RET_OK) return GRST_RET_FAILED; - iinfo = GRSTasn1SearchTaglist(taglist, lasttag, GRST_ASN1_COORDS_VOMS_INFO); - isig = GRSTasn1SearchTaglist(taglist, lasttag, GRST_ASN1_COORDS_VOMS_SIG); + snprintf(info_coords, sizeof(info_coords), + GRST_ASN1_COORDS_VOMS_INFO, acnumber); + iinfo = GRSTasn1SearchTaglist(taglist, lasttag, info_coords); + + snprintf(sig_coords, sizeof(sig_coords), + GRST_ASN1_COORDS_VOMS_SIG, acnumber); + isig = GRSTasn1SearchTaglist(taglist, lasttag, sig_coords); if ((iinfo < 0) || (isig < 0)) return GRST_RET_FAILED; @@ -433,6 +441,8 @@ static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, #if OPENSSL_VERSION_NUMBER >= 0x0090701fL EVP_MD_CTX_init(&ctx); EVP_VerifyInit_ex(&ctx, EVP_md5(), NULL); +#else + EVP_VerifyInit(&ctx, EVP_md5()); #endif EVP_VerifyUpdate(&ctx, @@ -448,7 +458,7 @@ static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, #if OPENSSL_VERSION_NUMBER >= 0x0090701fL EVP_MD_CTX_cleanup(&ctx); -#endif +#endif EVP_PKEY_free(prvkey); if (ret != 1) /* signature doesnt match, look for more */ @@ -479,7 +489,8 @@ static int GRSTx509VerifyVomsSig(time_t *time1_time, time_t *time2_time, /// Get the VOMS attributes in the given extension /* * Puts any VOMS credentials found into the Compact Creds string array - * starting at *creds. Always returns GRST_RET_OK. + * starting at *creds. Always returns GRST_RET_OK - even for invalid + * credentials, which are just ignored. */ int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen, @@ -487,14 +498,16 @@ int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen, X509_EXTENSION *ex, char *ucuserdn, char *vomsdir) { #define MAXTAG 500 -#define GRST_ASN1_COORDS_FQAN "-1-1-1-1-7-1-2-1-2-%d" -#define GRST_ASN1_COORDS_USER_DN "-1-1-1-1-2-1-1-1-1-%d-1-%d" -#define GRST_ASN1_COORDS_TIME1 "-1-1-1-1-6-1" -#define GRST_ASN1_COORDS_TIME2 "-1-1-1-1-6-2" +#define GRST_ASN1_COORDS_FQAN "-1-1-%d-1-7-1-2-1-2-%d" +#define GRST_ASN1_COORDS_USER_DN "-1-1-%d-1-2-1-1-1-1-%%d-1-%%d" +#define GRST_ASN1_COORDS_TIME1 "-1-1-%d-1-6-1" +#define GRST_ASN1_COORDS_TIME2 "-1-1-%d-1-6-2" ASN1_OCTET_STRING *asn1data; - char *asn1string, s[81], acuserdn[200], acvomsdn[200]; + char *asn1string, acuserdn[200], acvomsdn[200], + dn_coords[200], fqan_coords[200], time1_coords[200], + time2_coords[200]; long asn1length; - int lasttag=-1, itag, i; + int lasttag=-1, itag, i, acnumber = 1; struct GRSTasn1TagList taglist[MAXTAG+1]; time_t actime1, actime2, time_now; @@ -504,52 +517,58 @@ int GRSTx509ParseVomsExt(int *lastcred, int maxcreds, size_t credlen, GRSTasn1ParseDump(NULL, asn1string, asn1length, taglist, MAXTAG, &lasttag); - GRSTasn1GetX509Name(acuserdn, sizeof(acuserdn), GRST_ASN1_COORDS_USER_DN, - asn1string, taglist, lasttag); - if (GRSTx509NameCmp(ucuserdn, acuserdn) != 0) return GRST_RET_FAILED; + for (acnumber = 1; ; ++acnumber) /* go through ACs one by one */ + { + snprintf(dn_coords, sizeof(dn_coords), GRST_ASN1_COORDS_USER_DN, acnumber); + if (GRSTasn1GetX509Name(acuserdn, sizeof(acuserdn), dn_coords, + asn1string, taglist, lasttag) != GRST_RET_OK) break; - if (GRSTx509VerifyVomsSig(&time1_time, &time2_time, - asn1string, taglist, lasttag, vomsdir) - != GRST_RET_OK) return GRST_RET_FAILED; + if (GRSTx509NameCmp(ucuserdn, acuserdn) != 0) continue; - itag = GRSTasn1SearchTaglist(taglist, lasttag, GRST_ASN1_COORDS_TIME1); - actime1 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ - taglist[itag].headerlength], - taglist[itag].length); - if (actime1 > time1_time) time1_time = actime1; + if (GRSTx509VerifyVomsSig(&time1_time, &time2_time, + asn1string, taglist, lasttag, vomsdir, acnumber) + != GRST_RET_OK) continue; - itag = GRSTasn1SearchTaglist(taglist, lasttag, GRST_ASN1_COORDS_TIME2); - actime2 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ + snprintf(time1_coords, sizeof(time1_coords), GRST_ASN1_COORDS_TIME1, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time1_coords); + actime1 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ taglist[itag].headerlength], taglist[itag].length); - if (actime2 < time2_time) time2_time = actime2; + if (actime1 > time1_time) time1_time = actime1; - time(&time_now); - if ((time1_time > time_now) || (time2_time < time_now)) - return GRST_RET_OK; /* expiration isnt invalidity ...? */ + snprintf(time2_coords, sizeof(time2_coords), GRST_ASN1_COORDS_TIME2, acnumber); + itag = GRSTasn1SearchTaglist(taglist, lasttag, time2_coords); + actime2 = GRSTasn1TimeToTimeT(&asn1string[taglist[itag].start+ + taglist[itag].headerlength], + taglist[itag].length); + if (actime2 < time2_time) time2_time = actime2; - for (i=1; ; ++i) - { - sprintf(s, GRST_ASN1_COORDS_FQAN, i); - itag = GRSTasn1SearchTaglist(taglist, lasttag, s); + time(&time_now); + if ((time1_time > time_now) || (time2_time < time_now)) + continue; /* expiration isnt invalidity ...? */ - if (itag > -1) - { - if (*lastcred < maxcreds - 1) - { - ++(*lastcred); + for (i=1; ; ++i) + { + snprintf(fqan_coords, sizeof(fqan_coords), GRST_ASN1_COORDS_FQAN, acnumber, i); + itag = GRSTasn1SearchTaglist(taglist, lasttag, fqan_coords); - snprintf(&creds[*lastcred * (credlen + 1)], credlen+1, + if (itag > -1) + { + if (*lastcred < maxcreds - 1) + { + ++(*lastcred); + snprintf(&creds[*lastcred * (credlen + 1)], credlen+1, "VOMS %010lu %010lu 0 %.*s", time1_time, time2_time, taglist[itag].length, &asn1string[taglist[itag].start+ taglist[itag].headerlength]); - } - } - else break; + } + } + else break; + } } - + return GRST_RET_OK; } @@ -680,7 +699,8 @@ GRSTgaclCred *GRSTx509CompactToCred(char *grst_cred) */ int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen, - char *creds, STACK_OF(X509) *certstack, char *vomsdir) + char *creds, STACK_OF(X509) *certstack, char *vomsdir, + X509 *peercert) { int i, j, delegation = 0; char credtemp[credlen+1]; @@ -704,6 +724,19 @@ int GRSTx509CompactCreds(int *lastcred, int maxcreds, size_t credlen, /* found the 1st non-CA cert */ } + if (peercert != NULL) + { + if (usercert != NULL) /* found a (GSI proxy) cert after user cert */ + { + gsiproxycert = peercert; + ++delegation; + } + + if ((usercert == NULL) && + (GRSTx509IsCA(peercert) != GRST_RET_OK)) usercert = peercert; + /* found the 1st non-CA cert */ + } + if ((usercert == NULL) /* if no usercert ("EEC"), we're not interested */ || (snprintf(credtemp, credlen+1, "X509USER %010lu %010lu %d %s", diff --git a/org.gridsite.core/src/gsexec.c b/org.gridsite.core/src/gsexec.c index 292dcf3..4278c77 100644 --- a/org.gridsite.core/src/gsexec.c +++ b/org.gridsite.core/src/gsexec.c @@ -444,6 +444,8 @@ void mapdir_newlease(char *mapdir, char *encodedkey) } /****************************************************************************** +Based on gridmapdir_userid: + Function: gridmapdir_userid Description: This is equivalent to globus_gss_assist_gridmap but for the dynamic @@ -470,14 +472,13 @@ Returns: int GRSTexecGetMapping(char **target_uname, char **target_gname, char *mapdir, char *key) { - char *encodedkey; + char *encodedkey; + struct passwd *pw = NULL; if (key[0] != '/') return 1; /* must be a proper X.509 DN or path */ encodedkey = mapdir_urlencode(key); -log_err("encodedkey=%s\n", encodedkey); *target_uname = mapdir_otherlink(mapdir, encodedkey); -log_err("*target_uname=%s\n", *target_uname); if (*target_uname == NULL) /* maybe no lease yet */ { @@ -496,9 +497,56 @@ log_err("*target_uname=%s\n", *target_uname); free(encodedkey); -// nasty hack for now -*target_gname = strdup(*target_uname); - + /* + * Get the group name of target user. + (Contributed by Gerben Venekamp venekamp@nikhef.nl ) + */ + + if ((pw = getpwnam(*target_uname)) != NULL) + { + struct group grp = { NULL, NULL, -1, NULL }; + struct group *tst = NULL; + char tmp_buf[100]; + + /* + * NOTE: Do not use the getgrgid() function call! Calling this function + * will overwrite the contents of the internal buffer associated with + * this call. Hence, further down the execution path we will run into + * a wall, head first; simply because the guid has changed to that of + * the targer uid. The only solution out of the situation is avoiding + * the function call and manage the needed buffers ourselves. + */ + + switch (getgrgid_r(pw->pw_gid, &grp, tmp_buf, sizeof(tmp_buf), &tst)) + { + case 0: /* no error */ + *target_gname = strdup(grp.gr_name); + break; + case ERANGE: + log_err("The buffer for holding strings is too small " + "(%d byte now)\n", sizeof(tmp_buf)); + break; + default: + log_err("Could not get group name for user (%s)\n", + *target_uname); + } + + /* Test if all was well. */ + + if (target_gname == NULL) + { + exit(102); + } + } + else + { + log_err("Could not get info for the target user (%s)\n",*target_uname); + exit(102); + } + + log_no_err("target group name determined (%s -> %s)\n", + *target_uname, *target_gname); + return 0; } @@ -530,6 +578,7 @@ int main(int argc, char *argv[]) uid_t httpd_uid; /* uid for AP_HTTPD_USER */ gid_t httpd_gid; /* uid for AP_HTTPD_GROUP */ char *mapping_type; /* suexec / X509DN / directory */ + char *grst_cred_0; /* GRST_CRED_0 */ char *map_x509dn; /* DN to use as pool acct. key */ char *map_directory; /* directory as pool acct. key */ @@ -628,7 +677,6 @@ int main(int argc, char *argv[]) } mapping_type = getenv("GRST_EXEC_METHOD"); -// log_err("mapping_type from GRST_EXEC_METHOD=%s\n",mapping_type); if ((mapping_type == NULL) || (mapping_type[0] == '\0') || (strcasecmp(mapping_type, "suexec") == 0)) @@ -639,9 +687,9 @@ int main(int argc, char *argv[]) } else if (strcasecmp(mapping_type, "X509DN") == 0) { -// log_err("X509DN mapping type\n"); - if ((map_x509dn = getenv("GRST_CRED_0")) == NULL) + if ((grst_cred_0 = getenv("GRST_CRED_0")) == NULL) map_x509dn = getenv("SSL_CLIENT_S_DN"); + else map_x509dn = index(grst_cred_0, '/'); if ((map_x509dn == NULL) || (map_x509dn[0] == '\0')) { @@ -650,8 +698,8 @@ int main(int argc, char *argv[]) exit(151); } - if (GRSTexecGetMapping(&target_uname, &target_gname, - GRST_EXECMAPDIR, map_x509dn) + if (GRSTexecGetMapping(&target_uname, &target_gname, + GRST_EXECMAPDIR, map_x509dn) != 0) { log_err("GRSTexecGetMapping() failed mapping \"%s\"\n", diff --git a/org.gridsite.core/src/htcp b/org.gridsite.core/src/htcp index 8a64842f5685ba9462ec46f27388593f059ae03f..a2950e137dd84a7dee2849ee1e971b851e934ed7 100644 GIT binary patch literal 49931 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 literal 22680 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 diff --git a/org.gridsite.core/src/htcp.c b/org.gridsite.core/src/htcp.c index 3275806..763042a 100644 --- a/org.gridsite.core/src/htcp.c +++ b/org.gridsite.core/src/htcp.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2002-4, Andrew McNab, University of Manchester + Copyright (c) 2002-5, Andrew McNab, University of Manchester All rights reserved. Redistribution and use in source and binary forms, with or @@ -47,11 +47,17 @@ #include #include #include +#include +#include +#include +#include #include #include #include #include +#include "gridsite.h" + /* deal with older versions of libcurl and curl.h */ #ifndef CURLOPT_WRITEDATA @@ -72,6 +78,14 @@ #define HTCP_LIST 4 #define HTCP_LONGLIST 5 #define HTCP_MKDIR 6 +#define HTCP_MOVE 7 +#define HTCP_PING 8 +#define HTCP_FIND 9 + +#define HTCP_SITECAST_GROUPS 32 + +#define HTCP_HOST_CONF "/etc/htcp.conf" +#define HTCP_USER_CONF ".htcp.conf" struct grst_stream_data { char *source; char *destination; @@ -85,8 +99,12 @@ struct grst_stream_data { char *source; char *errorbuf; int noverify; int anonymous; - long long downgrade; - int verbose; } ; + int gridhttp; + int verbose; + int timeout; + char *groups; + int sitecast; + char *domain; } ; struct grst_index_blob { char *text; size_t used; @@ -100,13 +118,17 @@ struct grst_dir_list { char *filename; struct grst_header_data { int retcode; char *location; - char *gridauthonetime; + char *gridhttponetime; size_t length; int length_set; time_t modified; int modified_set; struct grst_stream_data *common_data; } ; +struct grst_sitecast_group { unsigned char quad1; unsigned char quad2; + unsigned char quad3; unsigned char quad4; + int port; int timewait; int ttl; }; + size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p) /* Find the values of the return code, Content-Length, Last-Modified and Location headers */ @@ -129,19 +151,22 @@ size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p) else if (strncmp(s, "Location: ", 10) == 0) { header_data->location = strdup(&s[10]); + + for (q=header_data->location; *q != '\0'; ++q) + if ((*q == '\r') || (*q == '\n')) *q = '\0'; if (header_data->common_data->verbose > 0) fprintf(stderr, "Received Location: %s\n", header_data->location); } - else if (strncmp(s, "Set-Cookie: GRID_AUTH_ONETIME=", 30) == 0) + else if (strncmp(s, "Set-Cookie: GRIDHTTP_ONETIME=", 29) == 0) { - header_data->gridauthonetime = strdup(&s[12]); - q = index(header_data->gridauthonetime, ';'); + header_data->gridhttponetime = strdup(&s[12]); + q = index(header_data->gridhttponetime, ';'); if (q != NULL) *q = '\0'; if (header_data->common_data->verbose > 0) - fprintf(stderr, "Received Grid Auth Cookie: %s\n", - header_data->gridauthonetime); + fprintf(stderr, "Received GridHTTP Auth Cookie: %s\n", + header_data->gridhttponetime); } else if (strncmp(s, "Last-Modified: ", 15) == 0) { @@ -200,8 +225,15 @@ int set_std_opts(CURL *easyhandle, struct grst_stream_data *common_data) } if (common_data->noverify) - curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 0); - else curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 2); + { + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 0); + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 0); + } + else + { + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYPEER, 2); + curl_easy_setopt(easyhandle, CURLOPT_SSL_VERIFYHOST, 2); + } return 1; } @@ -214,18 +246,17 @@ int do_copies(char *sources[], char *destination, CURL *easyhandle; struct stat statbuf; struct grst_header_data header_data; - struct curl_slist *dgheader_slist = NULL, *nodgheader_slist = NULL; + struct curl_slist *gh_header_slist = NULL, *nogh_header_slist = NULL; easyhandle = curl_easy_init(); - if (common_data->downgrade >= (long long) 0) + if (common_data->gridhttp) { - asprintf(&p, "HTTP-Downgrade-Size: %lld", common_data->downgrade); - dgheader_slist = curl_slist_append(dgheader_slist, p); + asprintf(&p, "Upgrade: GridHTTP/1.0"); + gh_header_slist = curl_slist_append(gh_header_slist, p); free(p); - nodgheader_slist = curl_slist_append(nodgheader_slist, - "HTTP-Downgrade-Size:"); + nogh_header_slist = curl_slist_append(nogh_header_slist, "Upgrade:"); } curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent); @@ -258,7 +289,7 @@ int do_copies(char *sources[], char *destination, } if (common_data->verbose > 0) - fprintf(stderr, "%s -> %s\n", sources[isrc], thisdestination); + fprintf(stderr, "Copy %s -> %s\n", sources[isrc], thisdestination); if (common_data->method == HTCP_GET) { @@ -275,17 +306,16 @@ int do_copies(char *sources[], char *destination, curl_easy_setopt(easyhandle, CURLOPT_WRITEDATA, common_data->fp); curl_easy_setopt(easyhandle, CURLOPT_URL, sources[isrc]); - if ((common_data->downgrade >= (long long) 0) && + if ((common_data->gridhttp) && (strncmp(sources[isrc], "https://", 8) == 0)) { if (common_data->verbose > 0) - fprintf(stderr, "Add HTTP-Downgrade-Size: %lld header\n", - common_data->downgrade); + fprintf(stderr, "Add Upgrade: GridHTTP/1.0\n"); - curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,dgheader_slist); + curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,gh_header_slist); } else - curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,nodgheader_slist); + curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,nogh_header_slist); } else if (common_data->method == HTCP_PUT) { @@ -312,30 +342,30 @@ int do_copies(char *sources[], char *destination, curl_easy_setopt(easyhandle, CURLOPT_INFILESIZE, statbuf.st_size); curl_easy_setopt(easyhandle, CURLOPT_UPLOAD, 1); - if (((long long) statbuf.st_size >= common_data->downgrade) && + if ((common_data->gridhttp) && (strncmp(thisdestination, "https://", 8) == 0)) - curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,dgheader_slist); + curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,gh_header_slist); else - curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,nodgheader_slist); + curl_easy_setopt(easyhandle,CURLOPT_HTTPHEADER,nogh_header_slist); } header_data.retcode = 0; header_data.location = NULL; - header_data.gridauthonetime = NULL; + header_data.gridhttponetime = NULL; header_data.common_data = common_data; thiserror = curl_easy_perform(easyhandle); fclose(common_data->fp); - if ((common_data->downgrade >= (long long) 0) && + if ((common_data->gridhttp) && (thiserror == 0) && (header_data.retcode == 302) && (header_data.location != NULL) && (strncmp(header_data.location, "http://", 7) == 0) && - (header_data.gridauthonetime != NULL)) + (header_data.gridhttponetime != NULL)) { if (common_data->verbose > 0) - fprintf(stderr, "... Found (%d)\nHTTP-Downgrade to %s\n", + fprintf(stderr, "... Found (%d)\nGridHTTP redirect to %s\n", header_data.retcode, header_data.location); /* try again with new URL and all the previous CURL options */ @@ -367,9 +397,9 @@ int do_copies(char *sources[], char *destination, header_data.retcode = 0; curl_easy_setopt(easyhandle, CURLOPT_URL, header_data.location); - curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, nodgheader_slist); + curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, nogh_header_slist); curl_easy_setopt(easyhandle, CURLOPT_COOKIE, - header_data.gridauthonetime); + header_data.gridhttponetime); thiserror = curl_easy_perform(easyhandle); fclose(common_data->fp); @@ -448,6 +478,64 @@ int do_deletes(char *sources[], struct grst_stream_data *common_data) return anyerror; } +int do_move(char *source, char *destination, + struct grst_stream_data *common_data) +{ + int anyerror = 0, thiserror; + char *destination_header; + CURL *easyhandle; + struct grst_header_data header_data; + struct curl_slist *header_slist = NULL; + + easyhandle = curl_easy_init(); + + header_data.common_data = common_data; + + easyhandle = curl_easy_init(); + + asprintf(&destination_header, "Destination: %s", destination); + header_slist = curl_slist_append(header_slist, destination_header); + curl_easy_setopt(easyhandle, CURLOPT_HTTPHEADER, header_slist); + + curl_easy_setopt(easyhandle, CURLOPT_USERAGENT, common_data->useragent); + if (common_data->verbose > 1) + curl_easy_setopt(easyhandle, CURLOPT_VERBOSE, 1); + + curl_easy_setopt(easyhandle, CURLOPT_HEADERFUNCTION, headers_callback); + curl_easy_setopt(easyhandle, CURLOPT_WRITEHEADER, &header_data); + + curl_easy_setopt(easyhandle, CURLOPT_ERRORBUFFER, common_data->errorbuf); + curl_easy_setopt(easyhandle, CURLOPT_CUSTOMREQUEST, "MOVE"); + curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1); + + set_std_opts(easyhandle, common_data); + + if (common_data->verbose > 0) + fprintf(stderr, "Moving %s to %s\n", source, destination); + + curl_easy_setopt(easyhandle, CURLOPT_URL, source); + + header_data.retcode = 0; + thiserror = curl_easy_perform(easyhandle); + + if ((thiserror != 0) || + (header_data.retcode < 200) || + (header_data.retcode >= 300)) + { + fprintf(stderr, "... curl error: %s (%d), HTTP error: %d\n", + common_data->errorbuf, thiserror, header_data.retcode); + + if (thiserror != 0) anyerror = thiserror; + else anyerror = header_data.retcode; + } + else if (common_data->verbose > 0) + fprintf(stderr, "... OK (%d)\n", header_data.retcode); + + curl_easy_cleanup(easyhandle); + + return anyerror; +} + int do_mkdirs(char *sources[], struct grst_stream_data *common_data) { int isrc, anyerror = 0, thiserror; @@ -500,6 +588,415 @@ int do_mkdirs(char *sources[], struct grst_stream_data *common_data) return anyerror; } +int do_ping(struct grst_stream_data *common_data_ptr) +{ + int request_length, response_length, i, ret, s, igroup; + struct sockaddr_in srv, from; + socklen_t fromlen; +#define MAXBUF 8192 + char *request, response[MAXBUF], *p; + GRSThtcpMessage msg; + struct timeval start_timeval, wait_timeval, response_timeval; + struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; + fd_set readsckts; + + /* parse common_data_ptr->groups */ + + p = common_data_ptr->groups; + igroup = -1; + + for (igroup=-1; igroup+1 < HTCP_SITECAST_GROUPS; ++igroup) + { + sitecast_groups[igroup+1].port = GRST_HTCP_PORT; + sitecast_groups[igroup+1].timewait = 1; + sitecast_groups[igroup+1].ttl = 1; + + ret = sscanf(p, "%d.%d.%d.%d:%d:%d:%d", + &(sitecast_groups[igroup+1].quad1), + &(sitecast_groups[igroup+1].quad2), + &(sitecast_groups[igroup+1].quad3), + &(sitecast_groups[igroup+1].quad4), + &(sitecast_groups[igroup+1].port), + &(sitecast_groups[igroup+1].ttl), + &(sitecast_groups[igroup+1].timewait)); + + if (ret == 0) break; /* end of list ? */ + + if (ret < 5) + { + fprintf(stderr, "Failed to parse multicast group " + "parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + ++igroup; + + if ((p = index(p, ',')) == NULL) break; + ++p; + } + + if (igroup == -1) + { + fprintf(stderr, "Failed to parse multicast group parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + fprintf(stderr, "Failed to open UDP socket\n"); + return CURLE_FAILED_INIT; + } + + /* loop through multicast groups and send off the NOP pings */ + + gettimeofday(&start_timeval, NULL); + + for (i=0; i <= igroup; ++i) + { + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(sitecast_groups[i].port); + srv.sin_addr.s_addr = htonl(sitecast_groups[i].quad1*0x1000000 + + sitecast_groups[i].quad2*0x10000 + + sitecast_groups[i].quad3*0x100 + + sitecast_groups[i].quad4); + + GRSThtcpNOPrequestMake(&request, &request_length, + (int) (start_timeval.tv_usec + i)); + + sendto(s, request, request_length, 0, (struct sockaddr *) &srv, + sizeof(srv)); + free(request); + } + + /* reusing wait_timeval is a Linux-specific feature of select() */ + wait_timeval.tv_sec = common_data_ptr->timeout + ? common_data_ptr->timeout : 60; + wait_timeval.tv_usec = 0; + + while ((wait_timeval.tv_sec > 0) || (wait_timeval.tv_usec > 0)) + { + FD_ZERO(&readsckts); + FD_SET(s, &readsckts); + + ret = select(s + 1, &readsckts, NULL, NULL, &wait_timeval); + gettimeofday(&response_timeval, NULL); + + if (ret > 0) + { + response_length = recvfrom(s, response, MAXBUF, + 0, &from, &fromlen); + + if ((GRSThtcpMessageParse(&msg, response, response_length) + == GRST_RET_OK) && + (msg.opcode == GRSThtcpNOPop) && (msg.rr == 1) && + (msg.trans_id >= (int) start_timeval.tv_usec) && + (msg.trans_id <= (int) (start_timeval.tv_usec + igroup))) + { + printf("%s:%d %.3fms\n", + inet_ntoa(from.sin_addr), + ntohs(from.sin_port), + (((long) 1000000 * response_timeval.tv_sec) + + ((long) response_timeval.tv_usec) - + ((long) 1000000 * start_timeval.tv_sec) - + ((long) start_timeval.tv_usec)) / 1000.0); + } + } + } + + return GRST_RET_OK; +} + +int do_finds(char *sources[], + struct grst_stream_data *common_data_ptr, int num) +{ + int isrc; + + int request_length, response_length, i, ret, s, igroup; + struct sockaddr_in srv, from; + socklen_t fromlen; +#define MAXBUF 8192 + char *request, response[MAXBUF], *p; + GRSThtcpMessage msg; + struct timeval start_timeval, wait_timeval; + struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; + fd_set readsckts; + + /* parse common_data_ptr->groups */ + + if (common_data_ptr->groups == NULL) + { + fprintf(stderr, "No multicast groups given\n"); + return CURLE_FAILED_INIT; + } + + p = common_data_ptr->groups; + igroup = -1; + + for (igroup=-1; igroup+1 < HTCP_SITECAST_GROUPS;) + { + sitecast_groups[igroup+1].port = GRST_HTCP_PORT; + sitecast_groups[igroup+1].timewait = 1; + sitecast_groups[igroup+1].ttl = 1; + + ret = sscanf(p, "%d.%d.%d.%d:%d:%d:%d", + &(sitecast_groups[igroup+1].quad1), + &(sitecast_groups[igroup+1].quad2), + &(sitecast_groups[igroup+1].quad3), + &(sitecast_groups[igroup+1].quad4), + &(sitecast_groups[igroup+1].port), + &(sitecast_groups[igroup+1].ttl), + &(sitecast_groups[igroup+1].timewait)); + + if (ret == 0) break; /* end of list ? */ + + if (ret < 5) + { + fprintf(stderr, "Failed to parse multicast group " + "parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + ++igroup; + + if ((p = index(p, ',')) == NULL) break; + ++p; + } + + if (igroup == -1) + { + fprintf(stderr, "Failed to parse multicast group parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + fprintf(stderr, "Failed to open UDP socket\n"); + return CURLE_FAILED_INIT; + } + + /* loop through multicast groups since we need to take each + ones timewait into account */ + + gettimeofday(&start_timeval, NULL); + + for (i=0; i <= igroup; ++i) + { + if (common_data_ptr->verbose) + fprintf(stderr, "Querying multicast group %d.%d.%d.%d:%d:%d:%d\n", + sitecast_groups[i].quad1, sitecast_groups[i].quad2, + sitecast_groups[i].quad3, sitecast_groups[i].quad4, + sitecast_groups[i].port, sitecast_groups[i].ttl, + sitecast_groups[i].timewait); + + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(sitecast_groups[i].port); + srv.sin_addr.s_addr = htonl(sitecast_groups[i].quad1*0x1000000 + + sitecast_groups[i].quad2*0x10000 + + sitecast_groups[i].quad3*0x100 + + sitecast_groups[i].quad4); + + /* send off queries, one for each source file */ + + for (isrc=0; sources[isrc] != NULL; ++isrc) + { + GRSThtcpTSTrequestMake(&request, &request_length, + (int) (start_timeval.tv_usec + isrc), + "GET", sources[isrc], ""); + + sendto(s, request, request_length, 0, + (struct sockaddr *) &srv, sizeof(srv)); + + free(request); + } + + /* reusing wait_timeval is a Linux-specific feature of select() */ + wait_timeval.tv_usec = 0; + wait_timeval.tv_sec = sitecast_groups[i].timewait; + + while ((wait_timeval.tv_sec > 0) || (wait_timeval.tv_usec > 0)) + { + FD_ZERO(&readsckts); + FD_SET(s, &readsckts); + + ret = select(s + 1, &readsckts, NULL, NULL, &wait_timeval); + + if (ret > 0) + { + response_length = recvfrom(s, response, MAXBUF, + 0, &from, &fromlen); + + if ((GRSThtcpMessageParse(&msg, response, response_length) + == GRST_RET_OK) && + (msg.opcode == GRSThtcpTSTop) && (msg.rr == 1) && + (msg.trans_id >= (int) start_timeval.tv_usec) && + (msg.trans_id < (int) (start_timeval.tv_usec + num)) && + (msg.resp_hdrs != NULL) && + (GRSThtcpCountstrLen(msg.resp_hdrs) > 12)) + { + if (num > 1) printf("%s -> %.*s\n", + sources[msg.trans_id - (int) start_timeval.tv_usec], + GRSThtcpCountstrLen(msg.resp_hdrs) - 12, + &(msg.resp_hdrs->text[10])); + else printf("%.*s\n", + GRSThtcpCountstrLen(msg.resp_hdrs) - 12, + &(msg.resp_hdrs->text[10])); + } + } + } + + } + + return GRST_RET_OK; +} + +int translate_sitecast_url(char **source_ptr, + struct grst_stream_data *common_data_ptr) +{ + int request_length, response_length, i, ret, s, igroup; + struct sockaddr_in srv, from; + socklen_t fromlen; +#define MAXBUF 8192 + char *request, response[MAXBUF], *p; + GRSThtcpMessage msg; + struct timeval start_timeval, wait_timeval; + struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; + fd_set readsckts; + + /* parse common_data_ptr->groups */ + + if (common_data_ptr->groups == NULL) + { + fprintf(stderr, "No multicast groups given\n"); + return CURLE_FAILED_INIT; + } + + p = common_data_ptr->groups; + igroup = -1; + + for (igroup=-1; igroup+1 < HTCP_SITECAST_GROUPS;) + { + sitecast_groups[igroup+1].port = GRST_HTCP_PORT; + sitecast_groups[igroup+1].timewait = 1; + sitecast_groups[igroup+1].ttl = 1; + + ret = sscanf(p, "%d.%d.%d.%d:%d:%d:%d", + &(sitecast_groups[igroup+1].quad1), + &(sitecast_groups[igroup+1].quad2), + &(sitecast_groups[igroup+1].quad3), + &(sitecast_groups[igroup+1].quad4), + &(sitecast_groups[igroup+1].port), + &(sitecast_groups[igroup+1].ttl), + &(sitecast_groups[igroup+1].timewait)); + + if (ret == 0) break; /* end of list ? */ + + if (ret < 5) + { + fprintf(stderr, "Failed to parse multicast group " + "parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + ++igroup; + + if ((p = index(p, ',')) == NULL) break; + ++p; + } + + if (igroup == -1) + { + fprintf(stderr, "Failed to parse multicast group parameter %s\n", p); + return CURLE_FAILED_INIT; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + fprintf(stderr, "Failed to open UDP socket\n"); + return CURLE_FAILED_INIT; + } + + /* loop through multicast groups since we need to take each + ones timewait into account */ + + gettimeofday(&start_timeval, NULL); + + for (i=0; i <= igroup; ++i) + { + if (common_data_ptr->verbose) + fprintf(stderr, "Querying multicast group %d.%d.%d.%d:%d:%d:%d\n", + sitecast_groups[i].quad1, sitecast_groups[i].quad2, + sitecast_groups[i].quad3, sitecast_groups[i].quad4, + sitecast_groups[i].port, sitecast_groups[i].ttl, + sitecast_groups[i].timewait); + + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(sitecast_groups[i].port); + srv.sin_addr.s_addr = htonl(sitecast_groups[i].quad1*0x1000000 + + sitecast_groups[i].quad2*0x10000 + + sitecast_groups[i].quad3*0x100 + + sitecast_groups[i].quad4); + + /* send off queries, one for each source file */ + + GRSThtcpTSTrequestMake(&request, &request_length, + (int) (start_timeval.tv_usec), + "GET", *source_ptr, ""); + + sendto(s, request, request_length, 0, + (struct sockaddr *) &srv, sizeof(srv)); + + free(request); + + /* reusing wait_timeval is a Linux-specific feature of select() */ + wait_timeval.tv_usec = 0; + wait_timeval.tv_sec = sitecast_groups[i].timewait; + + while ((wait_timeval.tv_sec > 0) || (wait_timeval.tv_usec > 0)) + { + FD_ZERO(&readsckts); + FD_SET(s, &readsckts); + + ret = select(s + 1, &readsckts, NULL, NULL, &wait_timeval); + + if (ret > 0) + { + response_length = recvfrom(s, response, MAXBUF, + 0, &from, &fromlen); + + if ((GRSThtcpMessageParse(&msg, response, response_length) + == GRST_RET_OK) && + (msg.opcode == GRSThtcpTSTop) && (msg.rr == 1) && + (msg.trans_id == (int) start_timeval.tv_usec) && + (msg.resp_hdrs != NULL) && + (GRSThtcpCountstrLen(msg.resp_hdrs) > 12)) + { + /* found one */ + + if (common_data_ptr->verbose > 0) + fprintf(stderr, "Sitecast %s -> %.*s\n", + *source_ptr, + GRSThtcpCountstrLen(msg.resp_hdrs) - 12, + &(msg.resp_hdrs->text[10])); + + free(*source_ptr); + + asprintf(source_ptr, "%.*s", + GRSThtcpCountstrLen(msg.resp_hdrs) - 12, + &(msg.resp_hdrs->text[10])); + + return GRST_RET_OK; + } + } + } + + } + + return GRST_RET_OK; +} + size_t rawindex_callback(void *ptr, size_t size, size_t nmemb, void *data) { if ( ((struct grst_index_blob *) data)->used + size * nmemb >= @@ -776,7 +1273,7 @@ int do_listings(char *sources[], struct grst_stream_data *common_data, curl_easy_setopt(easyhandle, CURLOPT_NOBODY, 1); } - header_data.gridauthonetime = NULL; + header_data.gridhttponetime = NULL; header_data.length_set = 0; header_data.modified_set = 0; header_data.retcode = 0; @@ -825,7 +1322,7 @@ int do_listings(char *sources[], struct grst_stream_data *common_data, asprintf(&s, "%s%s", sources[isrc], list[i].filename); curl_easy_setopt(easyhandle, CURLOPT_URL, s); - header_data.gridauthonetime = NULL; + header_data.gridhttponetime = NULL; header_data.length_set = 0; header_data.modified_set = 0; header_data.retcode = 0; @@ -970,14 +1467,7 @@ void printsyntax(char *argv0) "(Version: %s)\n", p, p, VERSION); } -int main(int argc, char *argv[]) -{ - char **sources, *destination = NULL, *executable, *p; - int c, i, option_index, anyerror; - struct stat statbuf; - struct grst_stream_data common_data; - struct passwd *userpasswd; - struct option long_options[] = { {"verbose", 0, 0, 'v'}, +struct option long_options[] = { {"verbose", 0, 0, 'v'}, {"cert", 1, 0, 0}, {"key", 1, 0, 0}, {"capath", 1, 0, 0}, @@ -987,12 +1477,101 @@ int main(int argc, char *argv[]) {"mkdir", 0, 0, 0}, {"no-verify", 0, 0, 0}, {"anon", 0, 0, 0}, - {"downgrade-size", 1, 0, 0}, -// {"streams", 1, 0, 0}, -// {"blocksize", 1, 0, 0}, -// {"recursive", 0, 0, 0}, + {"grid-http", 0, 0, 0}, + {"move", 0, 0, 0}, + {"ping", 0, 0, 0}, + {"groups", 1, 0, 0}, + {"timeout", 1, 0, 0}, + {"sitecast", 0, 0, 0}, + {"domain", 1, 0, 0}, + {"find", 0, 0, 0}, + {"conf", 1, 0, 0}, {0, 0, 0, 0} }; +int update_common_data(struct grst_stream_data *, int, char *); + +void parse_conf(struct grst_stream_data *common_data_ptr, char *conf_file) +{ + int option_index; + char line[1001], *p; + FILE *fp; + + fp = fopen(conf_file, "r"); + if (fp == NULL) + { + if (common_data_ptr->verbose) + fprintf(stderr, "Failed to open configuration file %s\n", conf_file); + return; + } + + if (common_data_ptr->verbose) + fprintf(stderr, "Opened configuration file %s\n", conf_file); + + while (fgets(line, sizeof(line), fp) != NULL) + { + if ((p = index(line, '\n')) != NULL) *p = '\0'; + + for (option_index=0; + long_options[option_index].name != NULL; ++option_index) + { + if (long_options[option_index].has_arg && + (strncmp(line, long_options[option_index].name, + strlen(long_options[option_index].name)) == 0) && + (line[strlen(long_options[option_index].name)] == '=')) + { + update_common_data(common_data_ptr, option_index, + strdup(&line[strlen(long_options[option_index].name) + 1])); + break; + } + + if (!long_options[option_index].has_arg && + (strcmp(line, long_options[option_index].name) == 0)) + { + update_common_data(common_data_ptr, option_index, ""); + break; + } + } + } + + fclose(fp); +} + +int update_common_data(struct grst_stream_data *common_data_ptr, + int option_index, char *optarg) +{ + if (option_index == 1) common_data_ptr->cert = optarg; + else if (option_index == 2) common_data_ptr->key = optarg; + else if (option_index == 3) common_data_ptr->capath = optarg; + else if (option_index == 4) common_data_ptr->method = HTCP_DELETE; + else if (option_index == 5) common_data_ptr->method = HTCP_LIST; + else if (option_index == 6) common_data_ptr->method = HTCP_LONGLIST; + else if (option_index == 7) common_data_ptr->method = HTCP_MKDIR; + else if (option_index == 8) common_data_ptr->noverify = 1; + else if (option_index == 9) common_data_ptr->anonymous = 1; + else if (option_index ==10) common_data_ptr->gridhttp = 1; + else if (option_index ==11) common_data_ptr->method = HTCP_MOVE; + else if (option_index ==12) common_data_ptr->method = HTCP_PING; + else if (option_index ==13) common_data_ptr->groups = optarg; + else if (option_index ==14) common_data_ptr->timeout = atoi(optarg); + else if (option_index ==15) common_data_ptr->sitecast = 1; + else if (option_index ==16) { common_data_ptr->sitecast = 1; + common_data_ptr->domain = optarg; } + else if (option_index ==17) common_data_ptr->method = HTCP_FIND; + /* option_index == 18 is used by the --conf command line-only option */ + else return GRST_RET_FAILED; + + return GRST_RET_OK; +} + +int main(int argc, char *argv[]) +{ + char **sources, *destination = NULL, *executable, *p, *htcp_conf; + int c, i, option_index, anyerror; + struct stat statbuf; + struct grst_stream_data common_data; + struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; + struct passwd *userpasswd; + #if (LIBCURL_VERSION_NUM < 0x070908) char *tmp_ca_roots = NULL; #endif @@ -1013,8 +1592,30 @@ int main(int argc, char *argv[]) common_data.verbose = 0; common_data.noverify = 0; common_data.anonymous = 0; - common_data.downgrade = (long long) -1; + common_data.gridhttp = 0; + common_data.groups = NULL; + common_data.timeout = 0; + common_data.sitecast = 0; + common_data.domain = NULL; + + if ((argc > 1) && ((strcmp(argv[1], "--verbose") == 0) || + (strcmp(argv[1], "-v") == 0))) common_data.verbose = 1; + + /* examine any configuration files */ + + parse_conf(&common_data, HTCP_HOST_CONF); + + userpasswd = getpwuid(geteuid()); + asprintf(&htcp_conf, "%s/%s", userpasswd->pw_dir, HTCP_USER_CONF); + parse_conf(&common_data, htcp_conf); + free(htcp_conf); + + htcp_conf = getenv("HTCP_CONF"); + if (htcp_conf != NULL) parse_conf(&common_data, htcp_conf); + + common_data.verbose = 0; + while (1) { option_index = 0; @@ -1024,16 +1625,8 @@ int main(int argc, char *argv[]) if (c == -1) break; else if (c == 0) { - if (option_index == 1) common_data.cert = optarg; - else if (option_index == 2) common_data.key = optarg; - else if (option_index == 3) common_data.capath = optarg; - else if (option_index == 4) common_data.method = HTCP_DELETE; - else if (option_index == 5) common_data.method = HTCP_LIST; - else if (option_index == 6) common_data.method = HTCP_LONGLIST; - else if (option_index == 7) common_data.method = HTCP_MKDIR; - else if (option_index == 8) common_data.noverify = 1; - else if (option_index == 9) common_data.anonymous = 1; - else if (option_index ==10) common_data.downgrade = atoll(optarg); + if (option_index == 18) parse_conf(&common_data, optarg); + else update_common_data(&common_data, option_index, optarg); } else if (c == 'v') ++(common_data.verbose); } @@ -1072,8 +1665,6 @@ int main(int argc, char *argv[]) common_data.cert = getenv("X509_USER_CERT"); common_data.key = getenv("X509_USER_KEY"); - userpasswd = getpwuid(geteuid()); - if ((common_data.cert == NULL) && (userpasswd != NULL) && (userpasswd->pw_dir != NULL)) @@ -1115,10 +1706,23 @@ int main(int argc, char *argv[]) else if (strcmp(executable,"htll")==0) common_data.method=HTCP_LONGLIST; else if (strcmp(executable,"htrm")==0) common_data.method=HTCP_DELETE; else if (strcmp(executable,"htmkdir")==0) common_data.method=HTCP_MKDIR; + else if (strcmp(executable,"htmv")==0) common_data.method=HTCP_MOVE; + else if (strcmp(executable,"htping")==0) common_data.method=HTCP_PING; + else if (strcmp(executable,"htfind")==0) common_data.method=HTCP_FIND; + } + + if (common_data.method == HTCP_PING) + { + if (common_data.groups != NULL) return do_ping(&common_data); + + fprintf(stderr, "Must specify at least one multicast group\n\n"); + printsyntax(argv[0]); + return CURLE_FAILED_INIT; } if ((common_data.method == HTCP_DELETE) || (common_data.method == HTCP_LIST) || + (common_data.method == HTCP_FIND) || (common_data.method == HTCP_MKDIR) || (common_data.method == HTCP_LONGLIST)) { @@ -1128,7 +1732,7 @@ int main(int argc, char *argv[]) printsyntax(argv[0]); return CURLE_URL_MALFORMAT; } - + sources = (char **) malloc(sizeof(char *) * (1 + argc - optind)); for (i=0; i < argc - optind; ++i) { @@ -1150,6 +1754,8 @@ int main(int argc, char *argv[]) anyerror = do_deletes(sources, &common_data); else if (common_data.method == HTCP_MKDIR) anyerror = do_mkdirs(sources, &common_data); + else if (common_data.method == HTCP_FIND) + anyerror = do_finds(sources, &common_data, argc - optind); else if (common_data.method == HTCP_LONGLIST) anyerror = do_listings(sources, &common_data, 1); else anyerror = do_listings(sources, &common_data, 0); @@ -1159,6 +1765,22 @@ int main(int argc, char *argv[]) return anyerror; } + if (common_data.method == HTCP_MOVE) + { + if (optind >= argc - 1) + { + fputs("Must give exactly 2 non-option arguments\n\n", stderr); + printsyntax(argv[0]); + return CURLE_URL_MALFORMAT; + } + + anyerror = do_move(argv[optind], argv[optind + 1], &common_data); + + if (anyerror > 99) anyerror = CURLE_HTTP_RETURNED_ERROR; + + return anyerror; + } + if (optind >= argc - 1) { fputs("Must give at least 2 non-option arguments\n\n", stderr); @@ -1171,8 +1793,8 @@ int main(int argc, char *argv[]) for (i=0; i < (argc - optind - 1); ++i) { if (strncmp(argv[optind + i], "file:", 5) == 0) - sources[i] = &argv[optind + i][5]; - else sources[i] = argv[optind + i]; + sources[i] = strdup(&argv[optind + i][5]); + else sources[i] = strdup(argv[optind + i]); if (sources[i][0] == '\0') { @@ -1184,9 +1806,24 @@ int main(int argc, char *argv[]) sources[i] = NULL; - if (strncmp(argv[optind + i], "file:", 5) == 0) - destination = &argv[optind + i][5]; - else destination = argv[optind + i]; + if (strncmp(argv[optind+i], "file:", 5) == 0) + { + if ((argv[optind+i][strlen(argv[optind+i]) - 1] != '/') && + (stat(&argv[optind + i][5], &statbuf) == 0) && + S_ISDIR(statbuf.st_mode)) + asprintf(&destination, "%s/", &argv[optind + i][5]); + else destination = strdup(&argv[optind + i][5]); + } + else if ((strncmp(argv[optind+i], "http://", 7) != 0) && + (strncmp(argv[optind+i], "https://", 8) != 0)) + { + if ((argv[optind+i][strlen(argv[optind+i]) - 1] != '/') && + (stat(argv[optind+i], &statbuf) == 0) && + S_ISDIR(statbuf.st_mode)) + asprintf(&destination, "%s/", argv[optind+i]); + else destination = strdup(argv[optind+i]); + } + else destination = strdup(argv[optind+i]); if (destination[0] == '\0') { @@ -1219,14 +1856,34 @@ int main(int argc, char *argv[]) return CURLE_URL_MALFORMAT; } - if ((common_data.method == HTCP_GET) && - ((strncmp(sources[i], "http://", 7) != 0) && - (strncmp(sources[i], "https://", 8) != 0))) + if (common_data.method == HTCP_GET) { - fputs("Cannot have both source and " + if ((strncmp(sources[i], "http://", 7) != 0) && + (strncmp(sources[i], "https://", 8) != 0)) + { + fputs("Cannot have both source and " "destination local (for now)\n\n",stderr); - printsyntax(argv[0]); - return CURLE_URL_MALFORMAT; + printsyntax(argv[0]); + return CURLE_URL_MALFORMAT; + } + + if ((common_data.sitecast) && + ((common_data.domain == NULL) || + + ((strncmp(sources[i], "http://", 7) == 0) && + (strncmp(&sources[i][7], common_data.domain, + strlen(common_data.domain)) == 0) && + ((sources[i][7+strlen(common_data.domain)] == ':') || + (sources[i][7+strlen(common_data.domain)] == '/'))) || + + ((strncmp(sources[i], "https://", 8) == 0) && + (strncmp(&sources[i][8], common_data.domain, + strlen(common_data.domain)) == 0) && + ((sources[i][8+strlen(common_data.domain)] == ':') || + (sources[i][8+strlen(common_data.domain)] == '/'))))) + { + translate_sitecast_url(&sources[i], &common_data); + } } } diff --git a/org.gridsite.core/src/mod_gridsite.c b/org.gridsite.core/src/mod_gridsite.c index 450640f..2d0a47b 100644 --- a/org.gridsite.core/src/mod_gridsite.c +++ b/org.gridsite.core/src/mod_gridsite.c @@ -1,6 +1,6 @@ /* - Copyright (c) 2003-5, Andrew McNab and Shiv Kaushal, University of Manchester - All rights reserved. + Copyright (c) 2003-5, Andrew McNab and Shiv Kaushal, + University of Manchester. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following @@ -42,6 +42,7 @@ #endif #include +#include #include #include @@ -56,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -63,6 +65,11 @@ #include #include +#include +#include +#include +#include + #include #include @@ -77,6 +84,25 @@ module AP_MODULE_DECLARE_DATA gridsite_module; +#define GRST_SITECAST_GROUPS 32 + +struct sitecast_group + { int socket; int quad1; int quad2; int quad3; int quad4; int port; }; + +#define GRST_SITECAST_ALIASES 32 + +struct sitecast_alias + { const char *sitecast_url; const char *local_path; server_rec *server; }; + +/* Globals, defined by main server directives in httpd.conf + These are assigned default values in create_gridsite_srv_config() */ + +int gridhttpport = 0; +char *onetimesdir = NULL; +char *sitecastdnlists = NULL; +struct sitecast_group sitecastgroups[GRST_SITECAST_GROUPS+1]; +struct sitecast_alias sitecastaliases[GRST_SITECAST_ALIASES]; + typedef struct { int auth; @@ -97,15 +123,13 @@ typedef struct char *editable; char *headfile; char *footfile; - int downgrade; - char *authcookiesdir; + int gridhttp; int soap2cgi; char *aclformat; char *execmethod; ap_unix_identity_t execugid; apr_fileperms_t diskmode; -} mod_gridsite_cfg; /* per-directory config choices */ - +} mod_gridsite_dir_cfg; /* per-directory config choices */ typedef struct { @@ -117,10 +141,10 @@ static const char Soap2cgiFilterName[]="Soap2cgiFilter"; static void mod_gridsite_soap2cgi_insert(request_rec *r) { - mod_gridsite_cfg *conf; + mod_gridsite_dir_cfg *conf; soap2cgi_ctx *ctx; - conf = (mod_gridsite_cfg *) ap_get_module_config(r->per_dir_config, + conf = (mod_gridsite_dir_cfg *) ap_get_module_config(r->per_dir_config, &gridsite_module); if (conf->soap2cgi) @@ -352,7 +376,7 @@ static apr_status_t mod_gridsite_soap2cgi_in(ap_filter_t *f, return APR_SUCCESS; } -char *make_admin_footer(request_rec *r, mod_gridsite_cfg *conf, +char *make_admin_footer(request_rec *r, mod_gridsite_dir_cfg *conf, int isdirectory) /* make string holding last modified text and admin links @@ -507,7 +531,7 @@ char *make_admin_footer(request_rec *r, mod_gridsite_cfg *conf, return out; } -int html_format(request_rec *r, mod_gridsite_cfg *conf) +int html_format(request_rec *r, mod_gridsite_dir_cfg *conf) /* try to do GridSite formatting of .html files (NOT .shtml etc) */ @@ -667,7 +691,7 @@ int html_format(request_rec *r, mod_gridsite_cfg *conf) return OK; } -int html_dir_list(request_rec *r, mod_gridsite_cfg *conf) +int html_dir_list(request_rec *r, mod_gridsite_dir_cfg *conf) /* output HTML directory listing, with level of formatting controlled by GridSiteHtmlFormat/conf->format @@ -866,7 +890,7 @@ int html_dir_list(request_rec *r, mod_gridsite_cfg *conf) return OK; } -int http_downgrade(request_rec *r, mod_gridsite_cfg *conf) +int http_gridhttp(request_rec *r, mod_gridsite_dir_cfg *conf) { int i; char *httpurl, *filetemplate, *cookievalue, *envname_i, @@ -882,22 +906,31 @@ int http_downgrade(request_rec *r, mod_gridsite_cfg *conf) sizeof(gridauthcookie)) != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Generated GridHTTP onetime passcode %016llx", gridauthcookie); + filetemplate = apr_psprintf(r->pool, "%s/%016llxXXXXXX", - ap_server_root_relative(r->pool, - conf->authcookiesdir), - gridauthcookie); - + ap_server_root_relative(r->pool, + onetimesdir), + gridauthcookie); + if (apr_file_mktemp(&fp, filetemplate, APR_CREATE | APR_WRITE | APR_EXCL, r->pool) != APR_SUCCESS) return HTTP_INTERNAL_SERVER_ERROR; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Created passcode file %s", filetemplate); expires_time = apr_time_now() + apr_time_from_sec(300); /* onetime cookies are valid for only 5 mins! */ - apr_file_printf(fp, "expires=%lu\ndomain=%s\npath=%s\nonetime=yes\n", - (time_t) apr_time_sec(expires_time), r->hostname, r->uri); + apr_file_printf(fp, + "expires=%lu\ndomain=%s\npath=%s\nonetime=yes\nmethod=%s\n", + (time_t) apr_time_sec(expires_time), + r->hostname, r->uri, r->method); + /* above variables are evaluated in order and method= MUST be last! */ for (i=0; ; ++i) { @@ -927,21 +960,25 @@ int http_downgrade(request_rec *r, mod_gridsite_cfg *conf) apr_table_add(r->headers_out, apr_pstrdup(r->pool, "Set-Cookie"), apr_psprintf(r->pool, - "GRID_AUTH_ONETIME=%s; " + "GRIDHTTP_ONETIME=%s; " "expires=%s; " "domain=%s; " "path=%s", cookievalue, expires_str, r->hostname, r->uri)); - - httpurl = apr_pstrcat(r->pool, "http://", r->hostname, - ap_escape_uri(r->pool, r->uri), NULL); + + if (gridhttpport != DEFAULT_HTTP_PORT) + httpurl = apr_psprintf(r->pool, "http://%s:%d%s", r->hostname, + gridhttpport, ap_escape_uri(r->pool, r->uri)); + else httpurl = apr_pstrcat(r->pool, "http://", r->hostname, + ap_escape_uri(r->pool, r->uri), NULL); + apr_table_setn(r->headers_out, apr_pstrdup(r->pool, "Location"), httpurl); r->status = HTTP_MOVED_TEMPORARILY; return OK; } -int http_put_method(request_rec *r, mod_gridsite_cfg *conf) +int http_put_method(request_rec *r, mod_gridsite_dir_cfg *conf) { char buf[2048]; size_t length; @@ -971,7 +1008,8 @@ int http_put_method(request_rec *r, mod_gridsite_cfg *conf) /* *** otherwise assume trying to create a regular file *** */ - if (apr_file_open(&fp, r->filename, APR_WRITE | APR_CREATE | APR_BUFFERED, + if (apr_file_open(&fp, r->filename, + APR_WRITE | APR_CREATE | APR_BUFFERED | APR_TRUNCATE, conf->diskmode, r->pool) != 0) return HTTP_INTERNAL_SERVER_ERROR; /* we force the permissions, rather than accept any existing ones */ @@ -979,6 +1017,7 @@ int http_put_method(request_rec *r, mod_gridsite_cfg *conf) apr_file_perms_set(r->filename, conf->diskmode); // TODO: need to add Range: support at some point too +// Also return 201 Created rather than 200 OK if not already existing retcode = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK); if (retcode == OK) @@ -1000,9 +1039,9 @@ int http_put_method(request_rec *r, mod_gridsite_cfg *conf) return retcode; } -int http_delete_method(request_rec *r, mod_gridsite_cfg *conf) +int http_delete_method(request_rec *r, mod_gridsite_dir_cfg *conf) { - if (remove(r->filename) != 0) return HTTP_FORBIDDEN; + if (apr_file_remove(r->filename, r->pool) != 0) return HTTP_FORBIDDEN; ap_set_content_length(r, 0); ap_set_content_type(r, "text/html"); @@ -1010,7 +1049,25 @@ int http_delete_method(request_rec *r, mod_gridsite_cfg *conf) return OK; } -static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_cfg *conf) +int http_move_method(request_rec *r, mod_gridsite_dir_cfg *conf) +{ + char *destination_translated = NULL; + + if (r->notes != NULL) destination_translated = + (char *) apr_table_get(r->notes, "GRST_DESTINATION_TRANSLATED"); + + + if ((destination_translated == NULL) || + (apr_file_rename(r->filename, destination_translated, r->pool) != 0)) + return HTTP_FORBIDDEN; + + ap_set_content_length(r, 0); + ap_set_content_type(r, "text/html"); + + return OK; +} + +static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_dir_cfg *conf) /* handler switch for directories */ @@ -1038,33 +1095,41 @@ static int mod_gridsite_dir_handler(request_rec *r, mod_gridsite_cfg *conf) return DECLINED; /* *** nothing to see here, move along *** */ } -static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_cfg *conf) +static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_dir_cfg *conf) /* one big handler switch for everything other than directories, since we might be responding to MIME * / * for local PUT, MOVE, COPY and DELETE, and GET inside ghost directories. */ { - char *downgradesize; - apr_off_t numericsize; + char *upgradeheader, *upgradespaced, *p; + const char *https_env; - /* *** is this a write method or HTTP downgrade? + /* *** is this a write method or GridHTTP HTTPS->HTTP redirection? only possible if GridSiteAuth on *** */ if (conf->auth) { - if ((conf->downgrade) && - ((downgradesize = (char *) apr_table_get(r->headers_in, - "HTTP-Downgrade-Size")) != NULL) && - ((numericsize = (apr_off_t) atoll(downgradesize)) >= 0) && + if ((conf->gridhttp) && + ((r->method_number == M_GET) || + ((r->method_number == M_PUT) && + (strstr(conf->methods, " PUT ") != NULL))) && + ((upgradeheader = (char *) apr_table_get(r->headers_in, + "Upgrade")) != NULL) && + ((https_env=apr_table_get(r->subprocess_env,"HTTPS")) != NULL) && + (strcasecmp(https_env, "on") == 0)) + { + upgradespaced = apr_psprintf(r->pool, " %s ", upgradeheader); + + for (p=upgradespaced; *p != '\0'; ++p) + if ((*p == ',') || (*p == '\t')) *p = ' '; // TODO: what if we're pointing at a CGI or some dynamic content??? - (((r->method_number == M_GET) && (r->finfo.size >= numericsize)) - || (r->method_number == M_PUT)) && + + if (strstr(upgradespaced, " GridHTTP/1.0 ") != NULL) + return http_gridhttp(r, conf); + } - (strcasecmp(apr_table_get(r->subprocess_env, "HTTPS"), "on") == 0)) - return http_downgrade(r, conf); - if ((r->method_number == M_PUT) && (conf->methods != NULL) && (strstr(conf->methods, " PUT " ) != NULL)) @@ -1074,6 +1139,11 @@ static int mod_gridsite_nondir_handler(request_rec *r, mod_gridsite_cfg *conf) (conf->methods != NULL) && (strstr(conf->methods, " DELETE ") != NULL)) return http_delete_method(r, conf); + + if ((r->method_number == M_MOVE) && + (conf->methods != NULL) && + (strstr(conf->methods, " MOVE ") != NULL)) + return http_move_method(r, conf); } /* *** check if a special ghost admin CGI *** */ @@ -1169,7 +1239,7 @@ static void recurse4dirlist(char *dirname, time_t *dirs_time, } static int mod_gridsite_dnlistsuri_dir_handler(request_rec *r, - mod_gridsite_cfg *conf) + mod_gridsite_dir_cfg *conf) /* virtual DN-list file lister: make all DN lists on the dn-lists path of this server appear to be in the dn-lists directory itself @@ -1381,7 +1451,7 @@ static char *recurse4file(char *dir, char *file, apr_pool_t *pool, } static int mod_gridsite_dnlistsuri_handler(request_rec *r, - mod_gridsite_cfg *conf) + mod_gridsite_dir_cfg *conf) /* virtual DN-list file generator */ @@ -1446,11 +1516,46 @@ static int mod_gridsite_dnlistsuri_handler(request_rec *r, return HTTP_NOT_FOUND; } +static void *create_gridsite_srv_config(apr_pool_t *p, server_rec *s) +{ + int i; + + if (!(s->is_virtual)) + { + gridhttpport = GRST_HTTP_PORT; + + onetimesdir = apr_pstrdup(p, "/var/www/onetimes"); + /* GridSiteOnetimesDir dir-path */ + + sitecastdnlists = NULL; + + sitecastgroups[0].quad1 = 0; + sitecastgroups[0].quad2 = 0; + sitecastgroups[0].quad3 = 0; + sitecastgroups[0].quad4 = 0; + sitecastgroups[0].port = GRST_HTCP_PORT; + /* GridSiteCastUniPort udp-port */ + + for (i=1; i <= GRST_SITECAST_GROUPS; ++i) + sitecastgroups[i].port = 0; + /* GridSiteCastGroup mcast-list */ + + for (i=1; i <= GRST_SITECAST_ALIASES; ++i) + { + sitecastaliases[i].sitecast_url = NULL; + sitecastaliases[i].local_path = NULL; + sitecastaliases[i].server = NULL; + } /* GridSiteCastAlias url path */ + } + + return NULL; +} + static void *create_gridsite_dir_config(apr_pool_t *p, char *path) { - mod_gridsite_cfg *conf = apr_palloc(p, sizeof(*conf)); + mod_gridsite_dir_cfg *conf = apr_palloc(p, sizeof(*conf)); - if (path == NULL) /* set up server defaults */ + if (path == NULL) /* set up document root defaults */ { conf->auth = 0; /* GridSiteAuth on/off */ conf->envs = 1; /* GridSiteEnvs on/off */ @@ -1478,9 +1583,7 @@ static void *create_gridsite_dir_config(apr_pool_t *p, char *path) conf->footfile = apr_pstrdup(p, GRST_FOOTFILE); /* GridSiteHeadFile and GridSiteFootFile file name */ - conf->downgrade = 0; /* GridSiteDowngrade on/off */ - conf->authcookiesdir = apr_pstrdup(p, "gridauthcookies"); - /* GridSiteAuthCookiesDir dir-path */ + conf->gridhttp = 0; /* GridSiteGridHTTP on/off */ conf->soap2cgi = 0; /* GridSiteSoap2cgi on/off */ conf->aclformat = apr_pstrdup(p, "GACL"); /* GridSiteACLFormat gacl/xacml */ @@ -1515,8 +1618,7 @@ static void *create_gridsite_dir_config(apr_pool_t *p, char *path) conf->editable = NULL; /* GridSiteEditable types */ conf->headfile = NULL; /* GridSiteHeadFile file name */ conf->footfile = NULL; /* GridSiteFootFile file name */ - conf->downgrade = UNSET; /* GridSiteDowngrade on/off */ - conf->authcookiesdir= NULL; /* GridSiteAuthCookiesDir dir-path */ + conf->gridhttp = UNSET; /* GridSiteGridHTTP on/off */ conf->soap2cgi = UNSET; /* GridSiteSoap2cgi on/off */ conf->aclformat = NULL; /* GridSiteACLFormat gacl/xacml */ conf->execmethod = NULL; /* GridSiteExecMethod */ @@ -1533,10 +1635,10 @@ static void *merge_gridsite_dir_config(apr_pool_t *p, void *vserver, void *vdirect) /* merge directory with server-wide directory configs */ { - mod_gridsite_cfg *conf, *server, *direct; + mod_gridsite_dir_cfg *conf, *server, *direct; - server = (mod_gridsite_cfg *) vserver; - direct = (mod_gridsite_cfg *) vdirect; + server = (mod_gridsite_dir_cfg *) vserver; + direct = (mod_gridsite_dir_cfg *) vdirect; conf = apr_palloc(p, sizeof(*conf)); if (direct->auth != UNSET) conf->auth = direct->auth; @@ -1594,12 +1696,8 @@ static void *merge_gridsite_dir_config(apr_pool_t *p, void *vserver, if (direct->footfile != NULL) conf->footfile = direct->footfile; else conf->footfile = server->footfile; - if (direct->downgrade != UNSET) conf->downgrade = direct->downgrade; - else conf->downgrade = server->downgrade; - - if (direct->authcookiesdir != NULL) - conf->authcookiesdir = direct->authcookiesdir; - else conf->authcookiesdir = server->authcookiesdir; + if (direct->gridhttp != UNSET) conf->gridhttp = direct->gridhttp; + else conf->gridhttp = server->gridhttp; if (direct->soap2cgi != UNSET) conf->soap2cgi = direct->soap2cgi; else conf->soap2cgi = server->soap2cgi; @@ -1628,34 +1726,86 @@ static void *merge_gridsite_dir_config(apr_pool_t *p, void *vserver, static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, const char *parm) { - int n; + int n, i; char *p; - if (strcasecmp(a->cmd->name, "GridSiteAdminFile") == 0) + if (strcasecmp(a->cmd->name, "GridSiteOnetimesDir") == 0) + { + if (a->server->is_virtual) + return "GridSiteOnetimesDir cannot be used inside a virtual server"; + + onetimesdir = apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteGridHTTPport") == 0) + { + gridhttpport = atoi(parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteCastDNlists") == 0) + { + if (a->server->is_virtual) + return "GridSiteDNlists cannot be used inside a virtual server"; + + sitecastdnlists = apr_pstrdup(a->pool, parm); + } + else if (strcasecmp(a->cmd->name, "GridSiteCastUniPort") == 0) + { + if (a->server->is_virtual) + return "GridSiteCastUniPort cannot be used inside a virtual server"; + + if (sscanf(parm, "%d", &(sitecastgroups[0].port)) != 1) + return "Failed parsing GridSiteCastUniPort numeric value"; + } + else if (strcasecmp(a->cmd->name, "GridSiteCastGroup") == 0) + { + if (a->server->is_virtual) + return "GridSiteCastGroup cannot be used inside a virtual server"; + + for (i=1; i <= GRST_SITECAST_GROUPS; ++i) + { + if (sitecastgroups[i].port == 0) /* a free slot */ + { + sitecastgroups[i].port = GRST_HTCP_PORT; + + if (sscanf(parm, "%d.%d.%d.%d:%d", + &(sitecastgroups[i].quad1), + &(sitecastgroups[i].quad2), + &(sitecastgroups[i].quad3), + &(sitecastgroups[i].quad4), + &(sitecastgroups[i].port)) < 4) + return "Failed parsing GridSiteCastGroup nnn.nnn.nnn.nnn[:port]"; + + break; + } + } + + if (i > GRST_SITECAST_GROUPS) + return "Maximum GridSiteCastGroup groups reached"; + } + else if (strcasecmp(a->cmd->name, "GridSiteAdminFile") == 0) { if (index(parm, '/') != NULL) return "/ not permitted in GridSiteAdminFile"; - ((mod_gridsite_cfg *) cfg)->adminfile = + ((mod_gridsite_dir_cfg *) cfg)->adminfile = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteAdminURI") == 0) { if (*parm != '/') return "GridSiteAdminURI must begin with /"; - ((mod_gridsite_cfg *) cfg)->adminuri = + ((mod_gridsite_dir_cfg *) cfg)->adminuri = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteHelpURI") == 0) { if (*parm != '/') return "GridSiteHelpURI must begin with /"; - ((mod_gridsite_cfg *) cfg)->helpuri = + ((mod_gridsite_dir_cfg *) cfg)->helpuri = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteDNlists") == 0) { - ((mod_gridsite_cfg *) cfg)->dnlists = + ((mod_gridsite_dir_cfg *) cfg)->dnlists = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteDNlistsURI") == 0) @@ -1663,15 +1813,15 @@ static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, if (*parm != '/') return "GridSiteDNlistsURI must begin with /"; if ((*parm != '\0') && (parm[strlen(parm) - 1] == '/')) - ((mod_gridsite_cfg *) cfg)->dnlistsuri = + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri = apr_pstrdup(a->pool, parm); else - ((mod_gridsite_cfg *) cfg)->dnlistsuri = + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri = apr_pstrcat(a->pool, parm, "/", NULL); } else if (strcasecmp(a->cmd->name, "GridSiteAdminList") == 0) { - ((mod_gridsite_cfg *) cfg)->adminlist = + ((mod_gridsite_dir_cfg *) cfg)->adminlist = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteGSIProxyLimit") == 0) @@ -1679,42 +1829,42 @@ static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, n = -1; if ((sscanf(parm, "%d", &n) == 1) && (n >= 0)) - ((mod_gridsite_cfg *) cfg)->gsiproxylimit = n; + ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit = n; else return "GridSiteGSIProxyLimit must be a number >= 0"; } else if (strcasecmp(a->cmd->name, "GridSiteUnzip") == 0) { if (*parm != '/') return "GridSiteUnzip must begin with /"; - ((mod_gridsite_cfg *) cfg)->unzip = + ((mod_gridsite_dir_cfg *) cfg)->unzip = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteMethods") == 0) { - ((mod_gridsite_cfg *) cfg)->methods = + ((mod_gridsite_dir_cfg *) cfg)->methods = apr_psprintf(a->pool, " %s ", parm); - for (p = ((mod_gridsite_cfg *) cfg)->methods; + for (p = ((mod_gridsite_dir_cfg *) cfg)->methods; *p != '\0'; ++p) if (*p == '\t') *p = ' '; } else if (strcasecmp(a->cmd->name, "GridSiteEditable") == 0) { - ((mod_gridsite_cfg *) cfg)->editable = + ((mod_gridsite_dir_cfg *) cfg)->editable = apr_psprintf(a->pool, " %s ", parm); - for (p = ((mod_gridsite_cfg *) cfg)->editable; + for (p = ((mod_gridsite_dir_cfg *) cfg)->editable; *p != '\0'; ++p) if (*p == '\t') *p = ' '; } else if (strcasecmp(a->cmd->name, "GridSiteHeadFile") == 0) { - ((mod_gridsite_cfg *) cfg)->headfile = + ((mod_gridsite_dir_cfg *) cfg)->headfile = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteFootFile") == 0) { - ((mod_gridsite_cfg *) cfg)->footfile = + ((mod_gridsite_dir_cfg *) cfg)->footfile = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteIndexHeader") == 0) @@ -1722,15 +1872,7 @@ static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, if (index(parm, '/') != NULL) return "/ not permitted in GridSiteIndexHeader"; - ((mod_gridsite_cfg *) cfg)->indexheader = - apr_pstrdup(a->pool, parm); - } - else if (strcasecmp(a->cmd->name, "GridSiteAuthCookiesDir") == 0) - { - if (index(parm, '/') != NULL) - return "/ not permitted in GridSiteAuthCookiesDir"; - - ((mod_gridsite_cfg *) cfg)->authcookiesdir = + ((mod_gridsite_dir_cfg *) cfg)->indexheader = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteACLFormat") == 0) @@ -1739,13 +1881,13 @@ static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, (strcasecmp(parm,"XACML") != 0)) return "GridsiteACLFormat must be either GACL or XACML"; - ((mod_gridsite_cfg *) cfg)->aclformat = apr_pstrdup(a->pool, parm); + ((mod_gridsite_dir_cfg *) cfg)->aclformat = apr_pstrdup(a->pool, parm); } else if (strcasecmp(a->cmd->name, "GridSiteExecMethod") == 0) { if (strcasecmp(parm, "nosetuid") == 0) { - ((mod_gridsite_cfg *) cfg)->execmethod = NULL; + ((mod_gridsite_dir_cfg *) cfg)->execmethod = NULL; return NULL; } @@ -1754,7 +1896,7 @@ static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, (strcasecmp(parm, "directory") != 0)) return "GridsiteExecMethod must be nosetuid, suexec, X509DN or directory"; - ((mod_gridsite_cfg *) cfg)->execmethod = apr_pstrdup(a->pool, parm); + ((mod_gridsite_dir_cfg *) cfg)->execmethod = apr_pstrdup(a->pool, parm); } return NULL; @@ -1763,6 +1905,8 @@ static const char *mod_gridsite_take1_cmds(cmd_parms *a, void *cfg, static const char *mod_gridsite_take2_cmds(cmd_parms *a, void *cfg, const char *parm1, const char *parm2) { + int i; + if (strcasecmp(a->cmd->name, "GridSiteUserGroup") == 0) { if (!(unixd_config.suexec_enabled)) @@ -1772,9 +1916,9 @@ static const char *mod_gridsite_take2_cmds(cmd_parms *a, void *cfg, /* NB ap_uname2id/ap_gname2id are NOT thread safe - but OK as long as not used in .htaccess, just at server start time */ - ((mod_gridsite_cfg *) cfg)->execugid.uid = ap_uname2id(parm1); - ((mod_gridsite_cfg *) cfg)->execugid.gid = ap_gname2id(parm2); - ((mod_gridsite_cfg *) cfg)->execugid.userdir = 0; + ((mod_gridsite_dir_cfg *) cfg)->execugid.uid = ap_uname2id(parm1); + ((mod_gridsite_dir_cfg *) cfg)->execugid.gid = ap_gname2id(parm2); + ((mod_gridsite_dir_cfg *) cfg)->execugid.userdir = 0; } else if (strcasecmp(a->cmd->name, "GridSiteDiskMode") == 0) { @@ -1789,12 +1933,25 @@ static const char *mod_gridsite_take2_cmds(cmd_parms *a, void *cfg, return "Second parameter of GridSiteDiskMode must be " "WorldNone or WorldRead!"; - ((mod_gridsite_cfg *) cfg)->diskmode = + ((mod_gridsite_dir_cfg *) cfg)->diskmode = APR_UREAD | APR_UWRITE | ( APR_GREAD * (strcasecmp(parm1, "GroupRead") == 0)) | ((APR_GREAD | APR_GWRITE) * (strcasecmp(parm1, "GroupWrite") == 0)) | ((APR_GREAD | APR_WREAD) * (strcasecmp(parm2, "WorldRead") == 0)); } + else if (strcasecmp(a->cmd->name, "GridSiteCastAlias") == 0) + { + for (i=0; i < GRST_SITECAST_ALIASES; ++i) /* look for free slot */ + { + if (sitecastaliases[i].sitecast_url == NULL) + { + sitecastaliases[i].sitecast_url = parm1; + sitecastaliases[i].local_path = parm2; + sitecastaliases[i].server = a->server; + break; + } + } + } return NULL; } @@ -1804,33 +1961,33 @@ static const char *mod_gridsite_flag_cmds(cmd_parms *a, void *cfg, { if (strcasecmp(a->cmd->name, "GridSiteAuth") == 0) { - ((mod_gridsite_cfg *) cfg)->auth = flag; + ((mod_gridsite_dir_cfg *) cfg)->auth = flag; } else if (strcasecmp(a->cmd->name, "GridSiteEnvs") == 0) { - ((mod_gridsite_cfg *) cfg)->envs = flag; + ((mod_gridsite_dir_cfg *) cfg)->envs = flag; } else if (strcasecmp(a->cmd->name, "GridSiteHtmlFormat") == 0) { - ((mod_gridsite_cfg *) cfg)->format = flag; + ((mod_gridsite_dir_cfg *) cfg)->format = flag; } else if (strcasecmp(a->cmd->name, "GridSiteIndexes") == 0) { - ((mod_gridsite_cfg *) cfg)->indexes = flag; + ((mod_gridsite_dir_cfg *) cfg)->indexes = flag; } else if (strcasecmp(a->cmd->name, "GridSiteLink") == 0) { - ((mod_gridsite_cfg *) cfg)->gridsitelink = flag; + ((mod_gridsite_dir_cfg *) cfg)->gridsitelink = flag; } - else if (strcasecmp(a->cmd->name, "GridSiteDowngrade") == 0) + else if (strcasecmp(a->cmd->name, "GridSiteGridHTTP") == 0) { // TODO: return error if try this on non-HTTPS virtual server - ((mod_gridsite_cfg *) cfg)->downgrade = flag; + ((mod_gridsite_dir_cfg *) cfg)->gridhttp = flag; } else if (strcasecmp(a->cmd->name, "GridSiteSoap2cgi") == 0) { - ((mod_gridsite_cfg *) cfg)->soap2cgi = flag; + ((mod_gridsite_dir_cfg *) cfg)->soap2cgi = flag; } return NULL; @@ -1879,11 +2036,22 @@ static const command_rec mod_gridsite_cmds[] = AP_INIT_TAKE1("GridSiteIndexHeader", mod_gridsite_take1_cmds, NULL, OR_FILEINFO, "filename of directory header"), - AP_INIT_FLAG("GridSiteDowngrade", mod_gridsite_flag_cmds, + AP_INIT_FLAG("GridSiteGridHTTP", mod_gridsite_flag_cmds, NULL, OR_FILEINFO, "on or off"), - AP_INIT_TAKE1("GridSiteAuthCookiesDir", mod_gridsite_take1_cmds, - NULL, OR_FILEINFO, "directory with Grid Auth Cookies"), - + AP_INIT_TAKE1("GridSiteGridHTTPport", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "GridHTTP port"), + AP_INIT_TAKE1("GridSiteOnetimesDir", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "directory with GridHTTP onetime passcodes"), + + AP_INIT_TAKE1("GridSiteCastDNlists", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "DN Lists directories search path for SiteCast"), + AP_INIT_TAKE1("GridSiteCastUniPort", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "UDP port for unicast/replies"), + AP_INIT_TAKE1("GridSiteCastGroup", mod_gridsite_take1_cmds, + NULL, RSRC_CONF, "multicast group[:port] to listen for HTCP on"), + AP_INIT_TAKE2("GridSiteCastAlias", mod_gridsite_take2_cmds, + NULL, RSRC_CONF, "URL and local path mapping"), + AP_INIT_FLAG("GridSiteSoap2cgi", mod_gridsite_flag_cmds, NULL, OR_FILEINFO, "on or off"), @@ -1906,11 +2074,11 @@ static const command_rec mod_gridsite_cmds[] = static int mod_gridsite_first_fixups(request_rec *r) { - mod_gridsite_cfg *conf; + mod_gridsite_dir_cfg *conf; if (r->finfo.filetype != APR_DIR) return DECLINED; - conf = (mod_gridsite_cfg *) + conf = (mod_gridsite_dir_cfg *) ap_get_module_config(r->per_dir_config, &gridsite_module); /* we handle DN Lists as regular files, even if they also match @@ -1927,6 +2095,41 @@ static int mod_gridsite_first_fixups(request_rec *r) return DECLINED; } +void GRST_creds_to_conn(conn_rec *conn, + STACK_OF(X509) *certstack, X509 *peercert) +{ + int i, lastcred; + const int maxcreds = 99; + const size_t credlen = 1024; + char creds[maxcreds][credlen+1], envname[14]; + + if ((certstack != NULL) && (conn->notes != NULL) && + (apr_table_get(conn->notes, "GRST_creds_to_conn") != NULL)) return; + + /* Put result of GRSTx509CompactCreds() into connection notes */ + + apr_table_set(conn->notes, "GRST_creds_to_conn", "yes"); + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server, + "set GRST_creds_to_conn"); + + if (GRSTx509CompactCreds(&lastcred, maxcreds, credlen, (char *) creds, + certstack, GRST_VOMS_DIR, peercert) == GRST_RET_OK) + { + for (i=0; i <= lastcred; ++i) + { + apr_table_setn(conn->notes, + apr_psprintf(conn->pool, "GRST_CRED_%d", i), + apr_pstrdup(conn->pool, creds[i])); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, conn->base_server, + "store GRST_CRED_%d=%s", i, creds[i]); + + } + + /* free remaining dup'd certs? */ + } +} + static int mod_gridsite_perm_handler(request_rec *r) /* Do authentication/authorization here rather than in the normal module @@ -1935,22 +2138,29 @@ static int mod_gridsite_perm_handler(request_rec *r) We also publish environment variables here if requested by GridSiteEnv. */ { - int retcode = DECLINED, i, n; + int retcode = DECLINED, i, n, file_is_acl = 0, + destination_is_acl = 0, proxylevel; char *dn, *p, envname[14], *grst_cred_0 = NULL, *dir_path, - *remotehost, s[99], *grst_cred_i, *file, *cookies, - *gridauthonetime, *cookiefile, oneline[1025], *key_i; + *remotehost, s[99], *grst_cred_i, *cookies, *file, + *gridauthonetime = NULL, *cookiefile, oneline[1025], *key_i, + *destination = NULL, *destination_uri = NULL, *querytmp, + *destination_prefix = NULL, *destination_translated = NULL; const char *content_type; time_t now, notbefore, notafter; apr_table_t *env; apr_finfo_t cookiefile_info; apr_file_t *fp; + request_rec *destreq; GRSTgaclCred *cred = NULL, *cred_0 = NULL; GRSTgaclUser *user = NULL; - GRSTgaclPerm perm = GRST_PERM_NONE; + GRSTgaclPerm perm = GRST_PERM_NONE, destination_perm = GRST_PERM_NONE; GRSTgaclAcl *acl = NULL; - mod_gridsite_cfg *cfg; + mod_gridsite_dir_cfg *cfg; + SSLConnRec *sslconn; + STACK_OF(X509) *certstack; + X509 *peercert; - cfg = (mod_gridsite_cfg *) + cfg = (mod_gridsite_dir_cfg *) ap_get_module_config(r->per_dir_config, &gridsite_module); if (cfg == NULL) return DECLINED; @@ -1961,109 +2171,34 @@ static int mod_gridsite_perm_handler(request_rec *r) env = r->subprocess_env; - if ((p = (char *) apr_table_get(r->headers_in, "Cookie")) != NULL) - { - cookies = apr_pstrcat(r->pool, " ", p, NULL); - gridauthonetime = strstr(cookies, " GRID_AUTH_ONETIME="); - - if (gridauthonetime != NULL) - { - for (p = &gridauthonetime[19]; (*p != '\0') && (*p != ';'); ++p) - if (!isalnum(*p)) *p = '_'; - - cookiefile = apr_psprintf(r->pool, "%s/%s", - ap_server_root_relative(r->pool, - cfg->authcookiesdir), - &gridauthonetime[19]); - - if ((apr_stat(&cookiefile_info , cookiefile, - APR_FINFO_TYPE, r->pool) == APR_SUCCESS) && - (cookiefile_info.filetype == APR_REG) && - (apr_file_open(&fp, cookiefile, APR_READ, 0, r->pool) - == APR_SUCCESS)) - { - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "Open Grid Auth Cookie file %s", cookiefile); - - while (apr_file_gets(oneline, - sizeof(oneline), fp) == APR_SUCCESS) - { - p = index(oneline, '\n'); - if (p != NULL) *p = '\0'; - - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, - "%s: %s", cookiefile, oneline); - - if ((strncmp(oneline, "expires=", 8) == 0) && - (apr_time_from_sec(atoll(&oneline[8])) < - apr_time_now())) - break; - else if ((strncmp(oneline, "domain=", 7) == 0) && - (strcmp(&oneline[7], r->hostname) != 0)) - break; /* exact needed in the version */ - else if ((strncmp(oneline, "path=", 5) == 0) && - (strcmp(&oneline[5], r->uri) != 0)) - break; - else if (strncmp(oneline, "onetime=yes", 11) == 0) - apr_file_remove(cookiefile, r->pool); - else if (strncmp(oneline, "GRST_CRED_", 10) == 0) - { - grst_cred_i = index(oneline, '='); - if (grst_cred_i == NULL) continue; - *grst_cred_i = '\0'; - ++grst_cred_i; - - i = atoi(&oneline[10]); - cred = GRSTx509CompactToCred(grst_cred_i); - - if (cred == NULL) continue; - - if ((i == 0) && (user == NULL)) - { - if (GRSTgaclCredGetDelegation(cred) - <= ((mod_gridsite_cfg *) cfg)->gsiproxylimit) - { - user = GRSTgaclUserNew(cred); - - ap_log_error(APLOG_MARK, APLOG_DEBUG, - 0, r->server, - "Using identity %s from " - "GRID_AUTH_ONETIME", - grst_cred_i); - - if (((mod_gridsite_cfg *) cfg)->envs) - apr_table_setn(env, oneline, grst_cred_i); - } - } - else if ((i > 0) && (user != NULL)) - { - GRSTgaclUserAddCred(user, cred); - - if (((mod_gridsite_cfg *) cfg)->envs) - apr_table_set(env,oneline,grst_cred_i); - } - } - } + /* do we need/have per-connection (SSL) cred variable(s)? */ + + sslconn = (SSLConnRec *) ap_get_module_config(r->connection->conn_config, + &ssl_module); - apr_file_close(fp); - } - } + if ((sslconn != NULL) && (sslconn->ssl != NULL) && + (r->connection->notes != NULL) && + (apr_table_get(r->connection->notes, "GRST_creds_to_conn") == NULL)) + { + certstack = SSL_get_peer_cert_chain(sslconn->ssl); + peercert = SSL_get_peer_certificate(sslconn->ssl); + + GRST_creds_to_conn(r->connection, certstack, peercert); } - - /* do we need/have per-connection (SSL) cred variable(s)? */ + proxylevel = ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit + 1; + if ((user == NULL) && (r->connection->notes != NULL) && ((grst_cred_0 = (char *) - apr_table_get(r->connection->notes, "GRST_CRED_0")) != NULL)) + apr_table_get(r->connection->notes, "GRST_CRED_0")) != NULL) && + (sscanf(grst_cred_0, "X509USER %*d %*d %d ", &proxylevel) == 1) && + (proxylevel <= ((mod_gridsite_dir_cfg *) cfg)->gsiproxylimit)) { - if (((mod_gridsite_cfg *) cfg)->envs) - apr_table_setn(env, "GRST_CRED_0", grst_cred_0); + apr_table_setn(env, "GRST_CRED_0", grst_cred_0); cred_0 = GRSTx509CompactToCred(grst_cred_0); - if ((cred_0 != NULL) && - (GRSTgaclCredGetDelegation(cred_0) - <= ((mod_gridsite_cfg *) cfg)->gsiproxylimit)) + if (cred_0 != NULL) { ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, "Using identity %s from SSL/TLS", grst_cred_0); @@ -2078,7 +2213,7 @@ static int mod_gridsite_perm_handler(request_rec *r) if (grst_cred_i = (char *) apr_table_get(r->connection->notes,envname)) { - if (((mod_gridsite_cfg *) cfg)->envs) + if (((mod_gridsite_dir_cfg *) cfg)->envs) apr_table_setn(env, apr_pstrdup(r->pool, envname), grst_cred_i); @@ -2091,32 +2226,186 @@ static int mod_gridsite_perm_handler(request_rec *r) } } - if ((user != NULL) && ((mod_gridsite_cfg *) cfg)->dnlists) - GRSTgaclUserSetDNlists(user, ((mod_gridsite_cfg *) cfg)->dnlists); + if ((user != NULL) && ((mod_gridsite_dir_cfg *) cfg)->dnlists) + GRSTgaclUserSetDNlists(user, ((mod_gridsite_dir_cfg *) cfg)->dnlists); - /* this checks for NULL arguments itself */ - if (GRSTgaclDNlistHasUser(((mod_gridsite_cfg *) cfg)->adminlist, user)) - perm = GRST_PERM_ALL; - else - { - remotehost = (char *) ap_get_remote_host(r->connection, + /* add DNS credential */ + + remotehost = (char *) ap_get_remote_host(r->connection, r->per_dir_config, REMOTE_DOUBLE_REV, NULL); - if ((remotehost != NULL) && (*remotehost != '\0')) - { - cred = GRSTgaclCredNew("dns"); - GRSTgaclCredAddValue(cred, "hostname", remotehost); + if ((remotehost != NULL) && (*remotehost != '\0')) + { + cred = GRSTgaclCredNew("dns"); + GRSTgaclCredAddValue(cred, "hostname", remotehost); + + if (user == NULL) user = GRSTgaclUserNew(cred); + else GRSTgaclUserAddCred(user, cred); + } + + /* check for Destination: header and evaluate if present */ - if (user == NULL) user = GRSTgaclUserNew(cred); - else GRSTgaclUserAddCred(user, cred); + if ((destination = (char *) apr_table_get(r->headers_in, + "Destination")) != NULL) + { + destination_prefix = apr_psprintf(r->pool, "https://%s:%d/", + r->server->server_hostname, (int) r->server->port); + + if (strncmp(destination_prefix, destination, + strlen(destination_prefix)) == 0) + destination_uri = &destination[strlen(destination_prefix)-1]; + else if ((int) r->server->port == 443) + { + destination_prefix = apr_psprintf(r->pool, "https://%s/", + r->server->server_hostname); + + if (strncmp(destination_prefix, destination, + strlen(destination_prefix)) == 0) + destination_uri = &destination[strlen(destination_prefix)-1]; } + + if (destination_uri != NULL) + { + destreq = ap_sub_req_method_uri("GET", destination_uri, r, NULL); + + if ((destreq != NULL) && (destreq->filename != NULL) + && (destreq->path_info != NULL)) + { + destination_translated = apr_pstrcat(r->pool, + destreq->filename, destreq->path_info, NULL); + apr_table_setn(r->notes, "GRST_DESTINATION_TRANSLATED", + destination_translated); + + if (((mod_gridsite_dir_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_DESTINATION_TRANSLATED", + destination_translated); + + p = rindex(destination_translated, '/'); + if ((p != NULL) && (strcmp(&p[1], GRST_ACL_FILE) == 0)) + destination_is_acl = 1; + } + } + } + + /* this checks for NULL arguments itself */ + if (GRSTgaclDNlistHasUser(((mod_gridsite_dir_cfg *) cfg)->adminlist, user)) + { + perm = GRST_PERM_ALL; + if (destination_translated != NULL) destination_perm = GRST_PERM_ALL; + } + else + { acl = GRSTgaclAclLoadforFile(r->filename); if (acl != NULL) perm = GRSTgaclAclTestUser(acl, user); + GRSTgaclAclFree(acl); + + if (destination_translated != NULL) + { + acl = GRSTgaclAclLoadforFile(destination_translated); + if (acl != NULL) destination_perm = GRSTgaclAclTestUser(acl, user); + GRSTgaclAclFree(acl); + + apr_table_setn(r->notes, "GRST_DESTINATION_PERM", + apr_psprintf(r->pool, "%d", destination_perm)); + + if (((mod_gridsite_dir_cfg *) cfg)->envs) + apr_table_setn(env, "GRST_DESTINATION_PERM", + apr_psprintf(r->pool, "%d", destination_perm)); + } } + + /* first look for GRIDHTTP_ONETIME cookie */ + + if ((p = (char *) apr_table_get(r->headers_in, "Cookie")) != NULL) + { + cookies = apr_pstrcat(r->pool, " ", p, NULL); + gridauthonetime = strstr(cookies, " GRIDHTTP_ONETIME="); + + if (gridauthonetime != NULL) + { + for (p = &gridauthonetime[18]; + (*p != '\0') && (*p != ';'); ++p) + if (!isalnum(*p)) *p = '\0'; + } + } + + /* then look for GRIDHTTP_ONETIME in QUERY_STRING ie after ? */ + + if (gridauthonetime == NULL) + { + if ((r->parsed_uri.query != NULL) && (r->parsed_uri.query[0] != '\0')) + { + querytmp = apr_pstrcat(r->pool,"&",r->parsed_uri.query,"&",NULL); + + gridauthonetime = strstr(querytmp, "&GRIDHTTP_ONETIME="); + + if (gridauthonetime != NULL) + { + for (p = &gridauthonetime[18]; + (*p != '\0') && (*p != '&'); ++p) + if (!isalnum(*p)) *p = '\0'; + } + } + } + + if ((gridauthonetime != NULL) && (gridauthonetime[0] != '\0')) + { + cookiefile = apr_psprintf(r->pool, "%s/%s", + ap_server_root_relative(r->pool, + onetimesdir), + &gridauthonetime[18]); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Opening GridHTTP onetime file %s", cookiefile); + + if ((apr_stat(&cookiefile_info, cookiefile, + APR_FINFO_TYPE, r->pool) == APR_SUCCESS) && + (cookiefile_info.filetype == APR_REG) && + (apr_file_open(&fp, cookiefile, APR_READ, 0, r->pool) + == APR_SUCCESS)) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "Reading GridHTTP onetime file %s", cookiefile); + + while (apr_file_gets(oneline, + sizeof(oneline), fp) == APR_SUCCESS) + { + p = index(oneline, '\n'); + if (p != NULL) *p = '\0'; + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "%s: %s", cookiefile, oneline); + + if ((strncmp(oneline, "expires=", 8) == 0) && + (apr_time_from_sec(atoll(&oneline[8])) < + apr_time_now())) + break; + else if ((strncmp(oneline, "domain=", 7) == 0) && + (strcmp(&oneline[7], r->hostname) != 0)) + break; /* exact needed in the version */ + else if ((strncmp(oneline, "path=", 5) == 0) && + (strcmp(&oneline[5], r->uri) != 0)) + break; + else if (strncmp(oneline, "onetime=yes", 11) == 0) + apr_file_remove(cookiefile, r->pool); + else if (strncmp(oneline, "method=PUT", 10) == 0) + perm |= GRST_PERM_WRITE; + else if (strncmp(oneline, "method=GET", 10) == 0) + perm |= GRST_PERM_READ; + } + + apr_file_close(fp); + } + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, + "After GACL/Onetime evaluation, GRST_PERM=%d", perm); + /* set permission and GACL environment variables */ + apr_table_setn(r->notes, "GRST_PERM", apr_psprintf(r->pool, "%d", perm)); - if (((mod_gridsite_cfg *) cfg)->envs) + if (((mod_gridsite_dir_cfg *) cfg)->envs) { apr_table_setn(env, "GRST_PERM", apr_psprintf(r->pool, "%d", perm)); @@ -2127,59 +2416,59 @@ static int mod_gridsite_perm_handler(request_rec *r) apr_table_setn(env, "GRST_DIR_PATH", dir_path); } - if (((mod_gridsite_cfg *) cfg)->helpuri != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->helpuri != NULL) apr_table_setn(env, "GRST_HELP_URI", - ((mod_gridsite_cfg *) cfg)->helpuri); + ((mod_gridsite_dir_cfg *) cfg)->helpuri); - if (((mod_gridsite_cfg *) cfg)->adminfile != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->adminfile != NULL) apr_table_setn(env, "GRST_ADMIN_FILE", - ((mod_gridsite_cfg *) cfg)->adminfile); + ((mod_gridsite_dir_cfg *) cfg)->adminfile); - if (((mod_gridsite_cfg *) cfg)->editable != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->editable != NULL) apr_table_setn(env, "GRST_EDITABLE", - ((mod_gridsite_cfg *) cfg)->editable); + ((mod_gridsite_dir_cfg *) cfg)->editable); - if (((mod_gridsite_cfg *) cfg)->headfile != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->headfile != NULL) apr_table_setn(env, "GRST_HEAD_FILE", - ((mod_gridsite_cfg *) cfg)->headfile); + ((mod_gridsite_dir_cfg *) cfg)->headfile); - if (((mod_gridsite_cfg *) cfg)->footfile != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->footfile != NULL) apr_table_setn(env, "GRST_FOOT_FILE", - ((mod_gridsite_cfg *) cfg)->footfile); + ((mod_gridsite_dir_cfg *) cfg)->footfile); - if (((mod_gridsite_cfg *) cfg)->dnlists != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->dnlists != NULL) apr_table_setn(env, "GRST_DN_LISTS", - ((mod_gridsite_cfg *) cfg)->dnlists); + ((mod_gridsite_dir_cfg *) cfg)->dnlists); - if (((mod_gridsite_cfg *) cfg)->dnlistsuri != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->dnlistsuri != NULL) apr_table_setn(env, "GRST_DN_LISTS_URI", - ((mod_gridsite_cfg *) cfg)->dnlistsuri); + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri); - if (((mod_gridsite_cfg *) cfg)->adminlist != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->adminlist != NULL) apr_table_setn(env, "GRST_ADMIN_LIST", - ((mod_gridsite_cfg *) cfg)->adminlist); + ((mod_gridsite_dir_cfg *) cfg)->adminlist); apr_table_setn(env, "GRST_GSIPROXY_LIMIT", apr_psprintf(r->pool, "%d", - ((mod_gridsite_cfg *)cfg)->gsiproxylimit)); + ((mod_gridsite_dir_cfg *)cfg)->gsiproxylimit)); - if (((mod_gridsite_cfg *) cfg)->unzip != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->unzip != NULL) apr_table_setn(env, "GRST_UNZIP", - ((mod_gridsite_cfg *) cfg)->unzip); + ((mod_gridsite_dir_cfg *) cfg)->unzip); - if (!(((mod_gridsite_cfg *) cfg)->gridsitelink)) + if (!(((mod_gridsite_dir_cfg *) cfg)->gridsitelink)) apr_table_setn(env, "GRST_NO_LINK", "1"); - if (((mod_gridsite_cfg *) cfg)->aclformat != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->aclformat != NULL) apr_table_setn(env, "GRST_ACL_FORMAT", - ((mod_gridsite_cfg *) cfg)->aclformat); + ((mod_gridsite_dir_cfg *) cfg)->aclformat); - if (((mod_gridsite_cfg *) cfg)->execmethod != NULL) + if (((mod_gridsite_dir_cfg *) cfg)->execmethod != NULL) { apr_table_setn(env, "GRST_EXEC_METHOD", - ((mod_gridsite_cfg *) cfg)->execmethod); + ((mod_gridsite_dir_cfg *) cfg)->execmethod); - if ((strcasecmp(((mod_gridsite_cfg *) cfg)->execmethod, + if ((strcasecmp(((mod_gridsite_dir_cfg *) cfg)->execmethod, "directory") == 0) && (r->filename != NULL)) { if ((r->content_type != NULL) && @@ -2200,29 +2489,25 @@ static int mod_gridsite_perm_handler(request_rec *r) apr_table_setn(env, "GRST_DISK_MODE", apr_psprintf(r->pool, "0x%04x", - ((mod_gridsite_cfg *)cfg)->diskmode)); + ((mod_gridsite_dir_cfg *)cfg)->diskmode)); } - if (((mod_gridsite_cfg *) cfg)->auth) + if (((mod_gridsite_dir_cfg *) cfg)->auth) { /* *** Check HTTP method to decide which perm bits to check *** */ - if (r->filename != NULL) - { - file = rindex(r->filename, '/'); - if (file != NULL) ++file; - else file = r->filename; - } - else file = NULL; + if ((r->filename != NULL) && + ((p = rindex(r->filename, '/')) != NULL) && + (strcmp(&p[1], GRST_ACL_FILE) == 0)) file_is_acl = 1; content_type = r->content_type; if ((content_type != NULL) && (strcmp(content_type, DIR_MAGIC_TYPE) == 0) && - (((mod_gridsite_cfg *) cfg)->dnlistsuri != NULL) && + (((mod_gridsite_dir_cfg *) cfg)->dnlistsuri != NULL) && (strncmp(r->uri, - ((mod_gridsite_cfg *) cfg)->dnlistsuri, - strlen(((mod_gridsite_cfg *) cfg)->dnlistsuri)) == 0) && - (strlen(r->uri) > strlen(((mod_gridsite_cfg *) cfg)->dnlistsuri))) + ((mod_gridsite_dir_cfg *) cfg)->dnlistsuri, + strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri)) == 0) && + (strlen(r->uri) > strlen(((mod_gridsite_dir_cfg *) cfg)->dnlistsuri))) content_type = "text/html"; if ( GRSTgaclPermHasNone(perm) || @@ -2248,14 +2533,23 @@ static int mod_gridsite_perm_handler(request_rec *r) ((r->method_number == M_POST) && !GRSTgaclPermHasRead(perm) ) || - (((r->method_number == M_PUT) || (r->method_number == M_DELETE)) && - !GRSTgaclPermHasWrite(perm) && - ((file == NULL) || (strcmp(file, GRST_ACL_FILE) != 0)) ) || - - (((r->method_number == M_PUT) || (r->method_number == M_DELETE)) && - !GRSTgaclPermHasAdmin(perm) && - (file != NULL) && - (strcmp(file, GRST_ACL_FILE) == 0) ) ) retcode = HTTP_FORBIDDEN; + (((r->method_number == M_PUT) || + (r->method_number == M_DELETE)) && + !GRSTgaclPermHasWrite(perm) && !file_is_acl) || + + ((r->method_number == M_MOVE) && + ((!GRSTgaclPermHasWrite(perm) && !file_is_acl) || + (!GRSTgaclPermHasAdmin(perm) && file_is_acl) || + (!GRSTgaclPermHasWrite(destination_perm) + && !destination_is_acl) || + (!GRSTgaclPermHasAdmin(destination_perm) + && destination_is_acl)) ) || + + (((r->method_number == M_PUT) || + (r->method_number == M_DELETE)) && + !GRSTgaclPermHasAdmin(perm) && file_is_acl) + + ) retcode = HTTP_FORBIDDEN; } return retcode; @@ -2311,6 +2605,7 @@ int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx) int errdepth = X509_STORE_CTX_get_error_depth(ctx); int returned_ok; int first_non_ca; + STACK_OF(X509) *certstack; /* * GSI Proxy user-cert-as-CA handling: @@ -2376,47 +2671,403 @@ int GRST_callback_SSLVerify_wrapper(int ok, X509_STORE_CTX *ctx) } else { - int i, lastcred; - STACK_OF(X509) *peer_certs; - const int maxcreds = 99; - const size_t credlen = 1024; - char creds[maxcreds][credlen+1], envname[14]; - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, "Valid certificate" " chain reported by GRSTx509CheckChain()"); - /* - * Always put result of GRSTx509CompactCreds() into environment - */ - if (peer_certs = (STACK_OF(X509) *) X509_STORE_CTX_get_chain(ctx)) - { - if (GRSTx509CompactCreds(&lastcred, maxcreds, credlen, - (char *) creds, peer_certs, GRST_VOMS_DIR) == GRST_RET_OK) - { - for (i=0; i <= lastcred; ++i) - { - apr_table_setn(conn->notes, - apr_psprintf(conn->pool, "GRST_CRED_%d", i), - apr_pstrdup(conn->pool, creds[i])); - - ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, s, - "store GRST_CRED_%d=%s", i, creds[i]); - } - } - /* free remaining dup'd certs? */ - } + /* Put result of GRSTx509CompactCreds() into connection notes */ + if ((certstack = + (STACK_OF(X509) *) X509_STORE_CTX_get_chain(ctx)) != NULL) + GRST_creds_to_conn(conn, certstack, NULL); } } return returned_ok; } +void sitecast_handle_NOP_request(server_rec *main_server, + GRSThtcpMessage *htcp_mesg, int igroup, + struct sockaddr_in *client_addr_ptr) +{ + int outbuf_len; + char *outbuf; + + if (GRSThtcpNOPresponseMake(&outbuf, &outbuf_len, + htcp_mesg->trans_id) == GRST_RET_OK) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast sends NOP response from port %d to %s:%d", + sitecastgroups[0].port, inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + + sendto(sitecastgroups[0].socket, outbuf, outbuf_len, 0, + client_addr_ptr, sizeof(struct sockaddr_in)); + + free(outbuf); + } +} + +void sitecast_handle_TST_GET(server_rec *main_server, + GRSThtcpMessage *htcp_mesg, int igroup, + struct sockaddr_in *client_addr_ptr) +{ + int i, outbuf_len, ialias, port; + char *filename, *outbuf, *location, *local_uri = NULL; + struct stat statbuf; + SSLSrvConfigRec *ssl_srv; + + /* check sanity of requested uri */ + + if (strncmp(htcp_mesg->uri->text, "http://", 7) == 0) + { + for (i=7; i < GRSThtcpCountstrLen(htcp_mesg->uri); ++i) + if (htcp_mesg->uri->text[i] == '/') + { + local_uri = &(htcp_mesg->uri->text[i]); + break; + } + } + else if (strncmp(htcp_mesg->uri->text, "https://", 8) == 0) + { + for (i=8; i < GRSThtcpCountstrLen(htcp_mesg->uri); ++i) + if (htcp_mesg->uri->text[i] == '/') + { + local_uri = &(htcp_mesg->uri->text[i]); + break; + } + } + + if (local_uri == NULL) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder only handles http(s):// (%*s requested by %s:%d)", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, + inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + return; + } + + /* find if any GridSiteCastAlias lines match */ + + for (ialias=0; ialias < GRST_SITECAST_ALIASES ; ++ialias) + { + if (sitecastaliases[ialias].sitecast_url == NULL) return; /* no match */ + + if ((strlen(sitecastaliases[ialias].sitecast_url) + <= GRSThtcpCountstrLen(htcp_mesg->uri)) && + (strncmp(sitecastaliases[ialias].sitecast_url, + htcp_mesg->uri->text, + strlen(sitecastaliases[ialias].sitecast_url))==0)) break; + } + + if (ialias == GRST_SITECAST_ALIASES) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder does not handle %*s requested by %s:%d", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, + inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + + return; /* no match */ + } + + /* convert URL to filename, using alias mapping */ + + asprintf(&filename, "%s%*s", + sitecastaliases[ialias].local_path, + GRSThtcpCountstrLen(htcp_mesg->uri) + - strlen(sitecastaliases[ialias].sitecast_url), + &(htcp_mesg->uri->text[strlen(sitecastaliases[ialias].sitecast_url)]) ); + + if (stat(filename, &statbuf) == 0) /* found file */ + { + ssl_srv = (SSLSrvConfigRec *) + ap_get_module_config(sitecastaliases[ialias].server->module_config, + &ssl_module); + + port = sitecastaliases[ialias].server->addrs->host_port; + if (port == 0) port = ((ssl_srv != NULL) && (ssl_srv->enabled)) + ? GRST_HTTPS_PORT : GRST_HTTP_PORT; + + asprintf(&location, "Location: http%s://%s:%d%s\r\n", + ((ssl_srv != NULL) && (ssl_srv->enabled)) ? "s" : "", + sitecastaliases[ialias].server->server_hostname, port, + local_uri); + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast finds %*s at %s, redirects with %s", + GRSThtcpCountstrLen(htcp_mesg->uri), + htcp_mesg->uri->text, filename, location); + + if (GRSThtcpTSTresponseMake(&outbuf, &outbuf_len, + htcp_mesg->trans_id, + location, "", "") == GRST_RET_OK) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast sends TST response from port %d to %s:%d", + sitecastgroups[0].port, inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + + sendto(sitecastgroups[0].socket, outbuf, outbuf_len, 0, + client_addr_ptr, sizeof(struct sockaddr_in)); + + free(outbuf); + } + + free(location); + } + + free(filename); +} + +void sitecast_handle_request(server_rec *main_server, + char *reqbuf, int reqbuf_len, int igroup, + struct sockaddr_in *client_addr_ptr) +{ + GRSThtcpMessage htcp_mesg; + + if (GRSThtcpMessageParse(&htcp_mesg,reqbuf,reqbuf_len) != GRST_RET_OK) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast responder rejects format of UDP message from %s:%d", + inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + return; + } + + if (htcp_mesg.rr != 0) /* ignore HTCP responses: we just do requests */ + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast responder ignores HTCP response from %s:%d", + inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + return; + } + + if (htcp_mesg.opcode == GRSThtcpNOPop) + { + sitecast_handle_NOP_request(main_server, &htcp_mesg, + igroup, client_addr_ptr); + return; + } + + if (htcp_mesg.opcode == GRSThtcpTSTop) + { + if (((GRSThtcpCountstrLen(htcp_mesg.method) == 3) && + (strncmp(htcp_mesg.method->text, "GET", 3) == 0)) || + ((GRSThtcpCountstrLen(htcp_mesg.method) == 4) && + (strncmp(htcp_mesg.method->text, "HEAD", 4) == 0))) + { + sitecast_handle_TST_GET(main_server, &htcp_mesg, + igroup, client_addr_ptr); + return; + } + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast responder rejects method %*s in TST message from %s:%d", + GRSThtcpCountstrLen(htcp_mesg.method), htcp_mesg.method->text, + inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); + return; + } + + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast does not implement HTCP op-code %d in message from %s:%d", + htcp_mesg.opcode, + inet_ntoa(client_addr_ptr->sin_addr), + ntohs(client_addr_ptr->sin_port)); +} + +void sitecast_responder(server_rec *main_server) +{ +#define GRST_SITECAST_MAXBUF 8192 + char reqbuf[GRST_SITECAST_MAXBUF], *p; + int n, reqbuf_len, i, j, igroup, + quad1, quad2, quad3, quad4, port, retval, client_addr_len; + struct sockaddr_in srv, client_addr; + struct ip_mreq mreq; + fd_set readsckts; + struct hostent *server_hostent; + + strcpy((char *) main_server->process->argv[0], "GridSiteCast UDP responder"); + + /* initialise unicast/replies socket first */ + + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(sitecastgroups[0].port); + + if ((server_hostent = gethostbyname(main_server->server_hostname)) == NULL) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast UDP Responder fails to look up servername %s", + main_server->server_hostname); + return; + } + + srv.sin_addr.s_addr = (u_int32_t) (server_hostent->h_addr_list[0][0]); + + if (((sitecastgroups[0].socket + = socket(AF_INET, SOCK_DGRAM, 0)) < 0) || + (bind(sitecastgroups[0].socket, + (struct sockaddr *) &srv, sizeof(srv)) < 0)) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "mod_gridsite: sitecast responder fails on unicast bind (%s)", + strerror(errno)); + return; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast UDP unicast/replies on %d.%d.%d.%d:%d", + server_hostent->h_addr_list[0][0], + server_hostent->h_addr_list[0][1], + server_hostent->h_addr_list[0][2], + server_hostent->h_addr_list[0][3], + sitecastgroups[0].port); + + /* initialise multicast listener sockets next */ + + for (i=1; (i <= GRST_SITECAST_GROUPS) && + (sitecastgroups[i].port != 0); ++i) + { + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(sitecastgroups[i].port); + srv.sin_addr.s_addr = htonl(sitecastgroups[i].quad1*0x1000000 + + sitecastgroups[i].quad2*0x10000 + + sitecastgroups[i].quad3*0x100 + + sitecastgroups[i].quad4); + + if (((sitecastgroups[i].socket + = socket(AF_INET, SOCK_DGRAM, 0)) < 0) || + (bind(sitecastgroups[i].socket, + (struct sockaddr *) &srv, sizeof(srv)) < 0)) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast UDP Responder fails on multicast bind (%s)", + strerror(errno)); + return; + } + + bzero(&mreq, sizeof(mreq)); + mreq.imr_multiaddr.s_addr = srv.sin_addr.s_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(sitecastgroups[i].socket, IPPROTO_IP, + IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) + { + ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server, + "SiteCast UDP Responder fails on setting multicast"); + return; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast UDP Responder listening on %d.%d.%d.%d:%d", + sitecastgroups[i].quad1, sitecastgroups[i].quad2, + sitecastgroups[i].quad3, sitecastgroups[i].quad4, sitecastgroups[i].port); + } + + while (1) /* **** main listening loop **** */ + { + /* set up bitmasks for select */ + + FD_ZERO(&readsckts); + + n = 0; + for (i=0; (i <= GRST_SITECAST_GROUPS) && + (sitecastgroups[i].port != 0); ++i) /* reset bitmask */ + { + FD_SET(sitecastgroups[i].socket, &readsckts); + if (sitecastgroups[i].socket > n) n = sitecastgroups[i].socket; + } + + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast UDP Responder waiting for requests"); + + if ((retval = select(n + 1, &readsckts, NULL, NULL, NULL)) < 1) + continue; /* < 1 on timeout or error */ + + for (igroup=0; (igroup <= GRST_SITECAST_GROUPS) && + (sitecastgroups[igroup].port != 0); ++igroup) + { + if (FD_ISSET(sitecastgroups[igroup].socket, &readsckts)) + { + client_addr_len = sizeof(client_addr); + + if ((reqbuf_len = recvfrom(sitecastgroups[igroup].socket, + reqbuf, GRST_SITECAST_MAXBUF, 0, + (struct sockaddr *) &client_addr, &client_addr_len)) >= 0) + { + ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, main_server, + "SiteCast receives UDP message from %s:%d " + "to %d.%d.%d.%d:%d", + inet_ntoa(client_addr.sin_addr), + ntohs(client_addr.sin_port), + sitecastgroups[igroup].quad1, + sitecastgroups[igroup].quad2, + sitecastgroups[igroup].quad3, + sitecastgroups[igroup].quad4, + sitecastgroups[igroup].port); + + sitecast_handle_request(main_server, reqbuf, + reqbuf_len, igroup, + &client_addr); + } + } + } + + } /* **** end of main listening loop **** */ +} + static int mod_gridsite_server_post_config(apr_pool_t *pPool, apr_pool_t *pLog, apr_pool_t *pTemp, server_rec *main_server) { SSL_CTX *ctx; SSLSrvConfigRec *sc; server_rec *this_server; + apr_proc_t *procnew = NULL; + apr_status_t status; + const char *userdata_key = "sitecast_init"; + + apr_pool_userdata_get((void **) &procnew, userdata_key, + main_server->process->pool); + + /* we only fork responder if one not already forked and we have at + least one GridSiteCastAlias defined. This means it is possible + to run a responder with no groups - listening on unicast only! */ + + if ((procnew == NULL) && + (sitecastaliases[0].sitecast_url != NULL)) + { + /* UDP multicast responder required but not yet started */ + + procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew)); + apr_pool_userdata_set((const void *) procnew, userdata_key, + apr_pool_cleanup_null, main_server->process->pool); + + status = apr_proc_fork(procnew, pPool); + + if (status < 0) + { + ap_log_error(APLOG_MARK, APLOG_CRIT, status, main_server, + "mod_gridsite: Failed to spawn SiteCast responder process"); + return HTTP_INTERNAL_SERVER_ERROR; + } + else if (status == APR_INCHILD) + { + ap_log_error(APLOG_MARK, APLOG_NOTICE, status, main_server, + "mod_gridsite: Spawning SiteCast responder process"); + sitecast_responder(main_server); + exit(-1); + } + + apr_pool_note_subprocess(main_server->process->pool, + procnew, APR_KILL_AFTER_TIMEOUT); + } + + /* continue with normal HTTP/HTTPS servers */ ap_add_version_component(pPool, apr_psprintf(pPool, "mod_gridsite/%s", VERSION)); @@ -2425,6 +3076,8 @@ static int mod_gridsite_server_post_config(apr_pool_t *pPool, this_server != NULL; this_server = this_server->next) { + /* we do some GridSite OpenSSL magic for HTTPS servers */ + sc = ap_get_module_config(this_server->module_config, &ssl_module); if ((sc != NULL) && @@ -2462,9 +3115,9 @@ static void mod_gridsite_child_init(apr_pool_t *pPool, server_rec *pServer) static int mod_gridsite_handler(request_rec *r) { - mod_gridsite_cfg *conf; + mod_gridsite_dir_cfg *conf; - conf = (mod_gridsite_cfg *) + conf = (mod_gridsite_dir_cfg *) ap_get_module_config(r->per_dir_config, &gridsite_module); if ((conf->dnlistsuri != NULL) && @@ -2484,9 +3137,9 @@ static int mod_gridsite_handler(request_rec *r) static ap_unix_identity_t *mod_gridsite_get_suexec_id_doer(const request_rec *r) { - mod_gridsite_cfg *conf; + mod_gridsite_dir_cfg *conf; - conf = (mod_gridsite_cfg *) + conf = (mod_gridsite_dir_cfg *) ap_get_module_config(r->per_dir_config, &gridsite_module); if ((conf->execugid.uid != UNSET) && @@ -2497,9 +3150,7 @@ static ap_unix_identity_t *mod_gridsite_get_suexec_id_doer(const request_rec *r) return &(conf->execugid); } - - - + return NULL; } @@ -2537,8 +3188,8 @@ module AP_MODULE_DECLARE_DATA gridsite_module = STANDARD20_MODULE_STUFF, create_gridsite_dir_config, /* dir config creater */ merge_gridsite_dir_config, /* dir merger */ - NULL, /* server config */ - NULL, /* merge server config */ + create_gridsite_srv_config, /* create server config */ + NULL, /* merge server config */ mod_gridsite_cmds, /* command apr_table_t */ register_hooks /* register hooks */ }; diff --git a/org.gridsite.core/src/showx509exts.c b/org.gridsite.core/src/showx509exts.c index d37dff4..86f0290 100644 --- a/org.gridsite.core/src/showx509exts.c +++ b/org.gridsite.core/src/showx509exts.c @@ -53,7 +53,7 @@ main() lasttag=-1; ex = X509_get_ext(cert, i); - + OBJ_obj2txt(s, sizeof(s), X509_EXTENSION_get_object(ex), 1); printf("%d OID=%s\n", i, s); @@ -73,8 +73,10 @@ main() ASN1_OBJECT *obj = NULL; const EVP_MD *m; EVP_MD_CTX ctx; + char creds[501][101]; + int lastcred = -1; - itag = GRSTasn1SearchTaglist(taglist, &lasttag, + itag = GRSTasn1SearchTaglist(taglist, lasttag, "-1-1-1-1-2-1-1-1-1-1-1-1"); X509_NAME *xname; @@ -91,10 +93,24 @@ main() printf("n=%d dn=%s obj2txt=%s\n", n, dn, OBJ_obj2txt(NULL,0,obj,1)); - GRSTasn1GetX509Name(buf, 99, "-1-1-1-1-2-1-1-1-1-%d-1-%d", p1, taglist, &lasttag); + GRSTasn1GetX509Name(buf, 99, "-1-1-1-1-2-1-1-1-1-%d-1-%d", + p1, taglist, lasttag); printf("%s\n", buf); - GRSTasn1GetX509Name(buf, 99, "-1-1-1-1-3-1-1-1-%d-1-%d", p1, taglist, &lasttag); + GRSTasn1GetX509Name(buf, 99, "-1-1-1-1-3-1-1-1-%d-1-%d", + p1, taglist, lasttag); printf("%s\n", buf); + + lastcred = -1; + ret = GRSTx509ParseVomsExt(&lastcred, 500, 100, creds, 0, 2000040861, + ex, + "/C=UK/O=eScience/OU=Manchester/L=HEP/CN=Andrew McNab", + "/etc/grid-security/vomsdir"); + + + printf("GRSTx509ParseVomsExt() returns %d, %d\n", ret, lastcred); + + for (j=0; j <= lastcred; ++j) + printf("cred=%d %s\n", j, creds[j]); /* m = EVP_md5(); -- 1.8.2.3